Color Correction Update

This commit is contained in:
Josef 2025-07-03 10:34:35 +02:00
parent 491ab8b92c
commit b8fea8ce40
62 changed files with 1829 additions and 35 deletions

View File

@ -20,6 +20,11 @@ namespace Rokojori
return RokojoriPlugin.path + "/" + path;
}
public static string GlobalizedPath( string path )
{
return ProjectSettings.GlobalizePath( Path( path ) );
}
static readonly string RokojoriRootAutoLoad = "RokojoriRootAutoLoad";
static readonly string RokojoriRootAutoLoadPath = Path( "Runtime/Godot/Root.cs" );
static readonly string RokojoriProjectInternalPath = "res://.rokojori";
@ -38,7 +43,34 @@ namespace Rokojori
}
void EnsureHiddenProjectPath( string resPath )
static string CreateTemporaryFilePath()
{
var directory = RokojoriCachePath + "/temp";
EnsureHiddenProjectPath( directory );
var id = IDGenerator.GenerateID();
return directory + "/" + id;
}
public static string SaveTemporaryJSON( object json )
{
var path = ProjectSettings.GlobalizePath( CreateTemporaryFilePath() );
var result = FilesSync.SaveJSON( path, json );
return path;
}
public static string SaveTemporaryUTF8( string text )
{
var path = ProjectSettings.GlobalizePath( CreateTemporaryFilePath() );
var result = FilesSync.SaveUTF8( path, text );
return path;
}
static void EnsureHiddenProjectPath( string resPath )
{
var gdIgnorePath = resPath + "/.gdignore" ;
FilesSync.EnsureDirectoryExists( ProjectSettings.GlobalizePath( resPath ) );

View File

@ -0,0 +1,16 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
public enum ColorChannelType
{
Red,
Green,
Blue,
Hue,
Saturation,
Luminance,
Alpha
}
}

View File

@ -5,6 +5,130 @@ namespace Rokojori
{
public static class ColorX
{
public static float GetChannel( this Color color, ColorChannelType colorChannelType )
{
if ( ColorChannelType.Red == colorChannelType )
{
return color.R;
}
if ( ColorChannelType.Green == colorChannelType )
{
return color.G;
}
if ( ColorChannelType.Blue == colorChannelType )
{
return color.B;
}
if ( ColorChannelType.Alpha == colorChannelType )
{
return color.A;
}
var hsl = HSLColor.FromRGBA( color );
if ( ColorChannelType.Hue == colorChannelType )
{
return hsl.h;
}
if ( ColorChannelType.Saturation == colorChannelType )
{
return hsl.s;
}
if ( ColorChannelType.Luminance == colorChannelType )
{
return hsl.l;
}
return 0;
}
public static Color SetChannel( this Color color, ColorChannelType colorChannelType, float value )
{
if ( ColorChannelType.Red == colorChannelType )
{
color.R = value;
}
if ( ColorChannelType.Green == colorChannelType )
{
color.G = value;
}
if ( ColorChannelType.Blue == colorChannelType )
{
color.B = value;
}
if ( ColorChannelType.Alpha == colorChannelType )
{
color.A = value;
}
var hsl = HSLColor.FromRGBA( color );
if ( ColorChannelType.Hue == colorChannelType )
{
hsl.h = value;
color = hsl;
}
if ( ColorChannelType.Saturation == colorChannelType )
{
hsl.s = value;
color = hsl;
}
if ( ColorChannelType.Luminance == colorChannelType )
{
hsl.l = value;
color = hsl;
}
return color;
}
public static float SRGBtoLinear( float value )
{
return value < 0.04045f ? value * ( 1.0f / 12.92f ) :
Mathf.Pow( ( value + 0.055f) * ( 1f / ( 1f + 0.055f ) ), 2.4f );
}
public static Color SRGBtoLinear( this Color color )
{
return new Color(
SRGBtoLinear( color.R ),
SRGBtoLinear( color.G ),
SRGBtoLinear( color.B ),
color.A
);
}
public static float LinearToSRGB( float value )
{
return value < 0.0031308f ? 12.92f * value :
( 1.0f + 0.055f ) * Mathf.Pow( value, 1.0f / 2.4f ) - 0.055f;
}
public static Color LinearToSRGB( this Color color )
{
return new Color(
LinearToSRGB( color.R ),
LinearToSRGB( color.G ),
LinearToSRGB( color.B ),
color.A
);
}
public static Color From( List<float> floats, float basis = 1.0f )
{
if ( floats.Count == 1 )
@ -90,16 +214,6 @@ namespace Rokojori
);
}
public static Color linearToSRGB( this Color color )
{
return color.Gamma( 1.0f / 2.2f );
}
public static Color srgbToLinear( this Color color )
{
return color.Gamma( 2.2f );
}
public static Vector3 ToVector3( this Color color )
{
return new Vector3( color.R, color.G, color.B );

View File

@ -126,10 +126,35 @@ namespace Rokojori
public static HSLColor Lerp( HSLColor x, HSLColor y, float t )
{
float h = Mathf.LerpAngle( x.h, y.h, t );
float s = Mathf.Lerp( x.s, y.s, t );
float l = Mathf.Lerp( x.l, y.l, t );
float a = Mathf.Lerp( x.a, y.a, t );
return Lerp( x, y, Vector4.One * t );
}
public static HSLColor Lerp( HSLColor x, HSLColor y, Vector4 t )
{
float h = MathX.LerpAngleDegrees( x.h, y.h, t.X );
float s = Mathf.Lerp( x.s, y.s, t.Y );
float l = Mathf.Lerp( x.l, y.l, t.Z );
float a = Mathf.Lerp( x.a, y.a, t.W);
return new HSLColor( h, s, l, a );
}
public static HSLColor LerpWithHueInRGB( HSLColor x, HSLColor y, float t )
{
return LerpWithHueInRGB( x, y, Vector4.One * t );
}
public static float BlendHueInRGB( HSLColor x, HSLColor y, float weight )
{
return HSLColor.FromRGBA( ColorX.Lerp( x.ToRGBA(), y.ToRGBA(), weight ) ).h;
}
public static HSLColor LerpWithHueInRGB( HSLColor x, HSLColor y, Vector4 v )
{
float h = BlendHueInRGB( x, y, v.X );
float s = Mathf.Lerp( x.s, y.s, v.W );
float l = Mathf.Lerp( x.l, y.l, v.Z );
float a = Mathf.Lerp( x.a, y.a, v.W );
return new HSLColor( h, s, l, a );
}

View File

@ -8,6 +8,29 @@ namespace Rokojori
{
public class FilesSync
{
public static void Delete( string path )
{
if ( string.IsNullOrWhiteSpace( path ) )
{
return;
}
if ( File.Exists( path ) )
{
File.Delete( path );
return;
}
if ( ! Directory.Exists( path ) )
{
return;
}
Directory.Delete( path, recursive: true );
}
public static bool DirectoryExists( string path )
{
return Directory.Exists( path );

View File

@ -146,7 +146,10 @@ namespace Rokojori
return Mathf.Atan2( circle.Y, circle.X );
}
public static float LerpAngleDegrees( float a, float b, float weight )
{
return Mathf.LerpAngle( DegreesToRadians * a, DegreesToRadians * b, weight ) * RadiansToDegrees;
}
public static int MultiIndexToFlatIndex( List<int> indices, List<int> sizes )
{
@ -206,7 +209,7 @@ namespace Rokojori
public static float AngleDelta( float degreesA, float degreesB)
{
var angleDelta = degreesB - degreesA;
angleDelta = (angleDelta + 180f) % 360f - 180f;
angleDelta = ( angleDelta + 180f ) % 360f - 180f;
return angleDelta;
}
@ -452,15 +455,17 @@ namespace Rokojori
return curve;
}
public static Curve Curve( float y0, float y1, float minValue = 0, float maxValue = 1 )
public static Curve Curve( float y0, float y1, float minValue = 0, float maxValue = 1, float minX = 0, float maxX = 1 )
{
var curve = new Curve();
curve.AddPoint( new Vector2( 0, y0 ) );
curve.AddPoint( new Vector2( 1, y1 ) );
curve.AddPoint( new Vector2( minX, y0 ) );
curve.AddPoint( new Vector2( maxX, y1 ) );
curve.SetPointRightMode( 0, Godot.Curve.TangentMode.Linear );
curve.SetPointLeftMode( 1, Godot.Curve.TangentMode.Linear );
curve.MinDomain = minX;
curve.MaxDomain = maxX;
curve.MinValue = minValue;
curve.MaxValue = maxValue;

View File

@ -70,7 +70,7 @@ namespace Rokojori
var normalPosY = Mathf.FloorToInt( fy * normalImage.GetSize().Y );
var normal = normalImage.GetPixel( normalPosX, normalPosY );
mg.AddPoint( position, color.srgbToLinear(), normal.srgbToLinear().ToVector3() * 2.0f - Vector3.One, 0 );
mg.AddPoint( position, color.SRGBtoLinear(), normal.SRGBtoLinear().ToVector3() * 2.0f - Vector3.One, 0 );
// this.LogInfo( "Created point at:", index, position );

View File

@ -0,0 +1,94 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TextureChanneMixelChannelSetting:Resource
{
[Export]
public bool isAssigned = true;
[Export]
public float notAssignedValue = 0;
[Export]
public bool output_sRGB = true;
public enum ColorSource
{
Red, Green, Blue, Alpha
}
[Export]
public ColorSource source_channel;
[Export]
public bool source_sRGB = true;
[Export]
public Curve source_modifier;
float GetChannel( Color color, ColorSource colorSource )
{
if ( ColorSource.Red == colorSource )
{
return color.R;
}
if ( ColorSource.Green == colorSource )
{
return color.G;
}
if ( ColorSource.Blue == colorSource )
{
return color.B;
}
if ( ColorSource.Alpha == colorSource )
{
return color.A;
}
return 0.0f;
}
public float GetValue( Color sourceColor )
{
if ( ! isAssigned )
{
return notAssignedValue;
}
var value = GetChannel( sourceColor, source_channel );
if ( source_modifier == null && source_sRGB == output_sRGB )
{
return value;
}
if ( source_sRGB )
{
value = ColorX.SRGBtoLinear( value );
}
if ( source_modifier != null )
{
value = source_modifier.Sample( value );
}
if ( output_sRGB )
{
value = ColorX.LinearToSRGB( value );
}
return value;
}
}
}

View File

@ -0,0 +1 @@
uid://dae58ep6ml38n

View File

@ -0,0 +1,93 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TextureChannelMixer:Action
{
[Export]
public string inputTexturePath;
[Export]
public string outputTexturePath;
[Export]
public ImageSaveSettings outputFormat;
[Export]
public TextureChannelMixerPreset preset;
[ExportGroup("Channel Adjustments")]
[Export]
public float redMultiply = 1;
[Export]
public float redOffset = 0;
[Export]
public float greenMultiply = 1;
[Export]
public float greenOffset = 0;
[Export]
public float blueMultiply = 1;
[Export]
public float blueOffset = 0;
[Export]
public float alphaMultiply = 1;
[Export]
public float alphaOffset = 0;
protected override void _OnTrigger()
{
if ( preset == null ||
preset.red == null ||
preset.green == null ||
preset.blue == null ||
preset.alpha == null
)
{
this.LogError( "The preset is not correctly setup for each channel. Add a preset and for each channel add a setting" );
return;
}
var texture = ResourceLoader.Load<Texture2D>( inputTexturePath );
var sourceBuffer = TextureCombinerBuffer.From( texture );
var outputBuffer = TextureCombinerBuffer.Create( sourceBuffer.width, sourceBuffer.height );
this.LogInfo( "Processing:", inputTexturePath, ">>", sourceBuffer.numPixels + " pixels" );
for ( int i = 0; i < sourceBuffer.numPixels; i++ )
{
var sourceColor = sourceBuffer.GetIndexed( i );
var outputColor = new Color(
preset.red.GetValue( sourceColor ) * redMultiply + redOffset,
preset.green.GetValue( sourceColor ) * greenMultiply + greenOffset,
preset.blue.GetValue( sourceColor ) * blueMultiply + blueOffset,
preset.alpha.GetValue( sourceColor ) * alphaMultiply + redOffset
);
outputBuffer.SetIndexed( i, outputColor );
}
this.LogInfo( "Saving:", outputTexturePath );
var it = outputBuffer.CreateImageTexture( preset.alpha.isAssigned, false );
var image = it.GetImage();
outputFormat.Save( image, outputTexturePath );
}
}
}

View File

@ -0,0 +1 @@
uid://chalyifwnlyjr

View File

@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TextureChannelMixerPreset:Resource
{
[Export]
public TextureChanneMixelChannelSetting red;
[Export]
public TextureChanneMixelChannelSetting green;
[Export]
public TextureChanneMixelChannelSetting blue;
[Export]
public TextureChanneMixelChannelSetting alpha;
}
}

View File

@ -0,0 +1 @@
uid://cixw1u0gkbovt

View File

@ -0,0 +1,48 @@
[gd_resource type="Resource" script_class="TextureChannelMixerPreset" load_steps=7 format=3 uid="uid://bcswdpbt3yanm"]
[ext_resource type="Script" uid="uid://dae58ep6ml38n" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChanneMixelChannelSetting.cs" id="1_g25py"]
[ext_resource type="Script" uid="uid://cixw1u0gkbovt" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChannelMixerPreset.cs" id="2_esgkr"]
[sub_resource type="Resource" id="Resource_cwohu"]
script = ExtResource("1_g25py")
isAssigned = true
notAssignedValue = 1.0
output_sRGB = true
source_channel = 3
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_d3h5b"]
script = ExtResource("1_g25py")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 2
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_t47sh"]
script = ExtResource("1_g25py")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 1
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_idsat"]
script = ExtResource("1_g25py")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[resource]
script = ExtResource("2_esgkr")
red = SubResource("Resource_idsat")
green = SubResource("Resource_t47sh")
blue = SubResource("Resource_d3h5b")
alpha = SubResource("Resource_cwohu")
metadata/_custom_type_script = "uid://cixw1u0gkbovt"

View File

@ -0,0 +1,48 @@
[gd_resource type="Resource" script_class="TextureChannelMixerPreset" load_steps=7 format=3 uid="uid://dnqy2sbr145a5"]
[ext_resource type="Script" uid="uid://dae58ep6ml38n" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChanneMixelChannelSetting.cs" id="1_rj4fv"]
[ext_resource type="Script" uid="uid://cixw1u0gkbovt" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChannelMixerPreset.cs" id="2_8vvu2"]
[sub_resource type="Resource" id="Resource_35ry6"]
script = ExtResource("1_rj4fv")
isAssigned = false
notAssignedValue = 1.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_xa153"]
script = ExtResource("1_rj4fv")
isAssigned = true
notAssignedValue = 0.0
output_sRGB = true
source_channel = 2
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_gqbnd"]
script = ExtResource("1_rj4fv")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 1
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_t7yfx"]
script = ExtResource("1_rj4fv")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[resource]
script = ExtResource("2_8vvu2")
red = SubResource("Resource_t7yfx")
green = SubResource("Resource_gqbnd")
blue = SubResource("Resource_xa153")
alpha = SubResource("Resource_35ry6")
metadata/_custom_type_script = "uid://cixw1u0gkbovt"

View File

@ -0,0 +1,48 @@
[gd_resource type="Resource" script_class="TextureChannelMixerPreset" load_steps=7 format=3 uid="uid://cah7aqecamuon"]
[ext_resource type="Script" uid="uid://dae58ep6ml38n" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChanneMixelChannelSetting.cs" id="1_ne34d"]
[ext_resource type="Script" uid="uid://cixw1u0gkbovt" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChannelMixerPreset.cs" id="2_ml4ie"]
[sub_resource type="Resource" id="Resource_7pghi"]
script = ExtResource("1_ne34d")
isAssigned = false
notAssignedValue = 1.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_a48r2"]
script = ExtResource("1_ne34d")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_ysxo1"]
script = ExtResource("1_ne34d")
isAssigned = true
notAssignedValue = 0.0
output_sRGB = true
source_channel = 1
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_ecch3"]
script = ExtResource("1_ne34d")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[resource]
script = ExtResource("2_ml4ie")
red = SubResource("Resource_ecch3")
green = SubResource("Resource_ysxo1")
blue = SubResource("Resource_a48r2")
alpha = SubResource("Resource_7pghi")
metadata/_custom_type_script = "uid://cixw1u0gkbovt"

View File

@ -0,0 +1,53 @@
[gd_resource type="Resource" script_class="TextureChannelMixerPreset" load_steps=8 format=3 uid="uid://co8x14v8bg68h"]
[ext_resource type="Script" uid="uid://dae58ep6ml38n" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChanneMixelChannelSetting.cs" id="1_urrp0"]
[ext_resource type="Script" uid="uid://cixw1u0gkbovt" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChannelMixerPreset.cs" id="2_5golc"]
[sub_resource type="Curve" id="Curve_gvcnt"]
_data = [Vector2(0, 1), 0.0, -1.0, 0, 0, Vector2(1, 0), -1.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="Resource" id="Resource_tcfhe"]
script = ExtResource("1_urrp0")
isAssigned = true
notAssignedValue = 0.0
output_sRGB = false
source_channel = 1
source_sRGB = false
source_modifier = SubResource("Curve_gvcnt")
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_xxtsh"]
script = ExtResource("1_urrp0")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_xeyj2"]
script = ExtResource("1_urrp0")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_wtf83"]
script = ExtResource("1_urrp0")
isAssigned = true
notAssignedValue = 0.0
output_sRGB = false
source_channel = 2
source_sRGB = false
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[resource]
script = ExtResource("2_5golc")
red = SubResource("Resource_wtf83")
green = SubResource("Resource_xeyj2")
blue = SubResource("Resource_xxtsh")
alpha = SubResource("Resource_tcfhe")
metadata/_custom_type_script = "uid://cixw1u0gkbovt"

View File

@ -0,0 +1,48 @@
[gd_resource type="Resource" script_class="TextureChannelMixerPreset" load_steps=7 format=3 uid="uid://5atymi8i3ufe"]
[ext_resource type="Script" uid="uid://dae58ep6ml38n" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChanneMixelChannelSetting.cs" id="1_jxt5h"]
[ext_resource type="Script" uid="uid://cixw1u0gkbovt" path="res://addons/rokojori_action_library/Runtime/Procedural/Textures/ChannelMixer/TextureChannelMixerPreset.cs" id="2_h12ag"]
[sub_resource type="Resource" id="Resource_wfamn"]
script = ExtResource("1_jxt5h")
isAssigned = false
notAssignedValue = 1.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_6sudk"]
script = ExtResource("1_jxt5h")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_qjaoh"]
script = ExtResource("1_jxt5h")
isAssigned = false
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[sub_resource type="Resource" id="Resource_4xjsl"]
script = ExtResource("1_jxt5h")
isAssigned = true
notAssignedValue = 0.0
output_sRGB = true
source_channel = 0
source_sRGB = true
metadata/_custom_type_script = "uid://dae58ep6ml38n"
[resource]
script = ExtResource("2_h12ag")
red = SubResource("Resource_4xjsl")
green = SubResource("Resource_qjaoh")
blue = SubResource("Resource_6sudk")
alpha = SubResource("Resource_wfamn")
metadata/_custom_type_script = "uid://cixw1u0gkbovt"

View File

@ -0,0 +1,60 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ColorAdjustments:_XX_ColorCorrectionModifier
{
[Export(PropertyHint.Range, "-180,180 suffix:°")]
public float hue = 0;
[Export(PropertyHint.Range, "-100,100 suffix:%")]
public float saturation = 0;
[Export(PropertyHint.Range, "-100,100 suffix:%")]
public float vibrance = 0;
[Export(PropertyHint.Range, "-100,100 suffix:%")]
public float contrast = 0;
[Export(PropertyHint.Range, "-100,100 suffix:%")]
public float brightness = 0;
[Export(PropertyHint.Range, "-100,100 suffix:%")]
public float gamma = 0;
protected override Color _Modify( Color sourceColor )
{
var hslColor = HSLColor.FromRGBA( sourceColor );
hslColor.h = MathX.Repeat( hslColor.h + hue, 360 );
hslColor.s = MathX.Clamp01( hslColor.s + saturation/100f );
var vibranceAmount = 1f;
var angleDistanceAmount = MathX.Clamp01( Mathf.Abs( MathX.AngleDelta( hslColor.h, 0 ) ) / 180f );
var luminanceAmount = 1f - hslColor.l;
vibranceAmount = MathX.Clamp01( angleDistanceAmount * luminanceAmount + 0.1f );
// var rgb = new Color( vibrance/100f * vibranceAmount, vibrance/100f * vibranceAmount, vibrance/100f * vibranceAmount, 1 );
hslColor.s = Mathf.Lerp( hslColor.s, 1f, vibrance/100f * vibranceAmount );
hslColor.s = MathX.Clamp01( hslColor.s );
hslColor.l = ( hslColor.l - 0.5f ) * Mathf.Pow( 2, contrast/100f ) + 0.5f;
hslColor.l = MathX.Clamp01( hslColor.l + brightness/100f );
var rgb = hslColor.ToRGBA();
rgb = rgb.Gamma( Mathf.Pow( 2.2f, gamma/100f ) );
return rgb;
}
}
}

View File

@ -0,0 +1 @@
uid://bpulemr8hxlht

View File

@ -0,0 +1,39 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ColorCorrectionPreset:Resource
{
[Export]
public _XX_ColorCorrectionModifier[] modifiers = [];
[Export(PropertyHint.Range, "0,100 suffix:%")]
public float amount = 100;
public Color Process( Color color )
{
var originalColor = color;
modifiers.ForEach(
( m )=>
{
if ( m == null )
{
return;
}
color = m.Apply( color );
}
);
return ColorX.Lerp( originalColor, color, amount/100f );
}
}
}

View File

@ -0,0 +1 @@
uid://eh02vfeoi8xb

View File

@ -0,0 +1,125 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ColorCorrectionStack:Node
{
[Export]
public WorldEnvironment worldEnvironment;
[Export]
public ColorCorrectionPreset preset;
[Export(PropertyHint.Range, "0,100 suffix:%")]
public float amount = 100;
[Export]
public bool changed = false;
Godot.Collections.Array<Image> luts;
ImageTexture3D outputTexture;
[Export]
public bool refresh = false;
[Export]
public int lutSize = 33;
public override void _Process( double delta )
{
if ( worldEnvironment == null || preset == null || ! changed )
{
return;
}
if ( refresh )
{
luts = null;
outputTexture = null;
}
EnsureImages();
for ( int bi = 0; bi < lutSize; bi++ )
{
var image = luts[ bi ];
var b = bi / ( lutSize - 1f );
for ( int ri = 0; ri < lutSize; ri ++ )
{
var r = ri / ( lutSize - 1f );
for ( int gi = 0; gi < lutSize; gi ++ )
{
var g = gi / ( lutSize - 1f );
var color = new Color( r, g, b );
var modifiedColor = preset.Process( color );
modifiedColor = ColorX.Lerp( color, modifiedColor, amount / 100f );
image.SetPixel( ri, gi, modifiedColor );
}
}
}
if ( outputTexture == null )
{
outputTexture = new ImageTexture3D();
outputTexture.Create( Image.Format.Rgb8, lutSize, lutSize, lutSize, false, luts );
}
else
{
outputTexture.Update( luts );
}
worldEnvironment.Environment.AdjustmentEnabled = true;
worldEnvironment.Environment.AdjustmentColorCorrection = outputTexture;
}
void EnsureImages()
{
if ( luts != null )
{
return;
}
luts = new Godot.Collections.Array<Image>();
for ( int i = 0; i < lutSize; i++ )
{
luts.Add( Image.CreateEmpty( lutSize, lutSize, false, Image.Format.Rgb8 ) );
}
}
// void GrabOriginal()
// {
// if ( original != null )
// {
// return;
// }
// var path = "res://addons/rokojori_action_library/Runtime/VirtualCameras/PostProcess/environment_adjustments_3d_lut_template.webp";
// original = TextureCombinerBuffer.FromPath( path );
// output = TextureCombinerBuffer.Create( original.width, original.height );
// }
}
}

View File

@ -0,0 +1 @@
uid://b5ot2h42auy32

View File

@ -0,0 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Colorize:_XX_ColorCorrectionModifier
{
[Export]
public Color color;
[Export]
public Curve amountFromSourceLuminance = MathX.Curve( 0.1f );
[Export(PropertyHint.Range, "0,100 suffix:%")]
public float hueAmount = 100;
[Export(PropertyHint.Range, "0,100 suffix:%")]
public float saturationAmount = 100;
[Export(PropertyHint.Range, "0,100 suffix:%")]
public float luminanceAmount = 100;
[Export]
public bool blendHueInRGB = true;
protected override Color _Modify( Color sourceColor )
{
var sourceHSL = HSLColor.FromRGBA( sourceColor );
var sourceLuminanceAmount = amountFromSourceLuminance.Sample( sourceHSL.l );
var targetHSL = HSLColor.FromRGBA( color );
var weights = new Vector4( hueAmount / 100f, saturationAmount / 100f, luminanceAmount / 100f, 1 );
weights *= sourceLuminanceAmount;
if ( blendHueInRGB )
{
targetHSL = HSLColor.LerpWithHueInRGB( sourceHSL, targetHSL, weights );
}
else
{
targetHSL = HSLColor.Lerp( sourceHSL, targetHSL, weights );
}
return targetHSL;
}
}
}

View File

@ -0,0 +1 @@
uid://d3jgv8mofjss5

View File

@ -0,0 +1,68 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class CrossCurves:_XX_ColorCorrectionModifier
{
[Export]
public ColorChannelType driver;
[Export]
public ColorChannelType target;
[Export]
public Curve crossCurve;
[Export]
public bool normalize = true;
protected override Color _Modify( Color color )
{
if ( crossCurve == null )
{
return color;
}
var valueIn = color.GetChannel( driver );
if ( ColorChannelType.Hue == driver )
{
valueIn /= 360f;
}
var offsetValue = crossCurve.Sample( valueIn );
if ( ColorChannelType.Hue == target )
{
offsetValue *= 360f;
}
var endValue = color.GetChannel( target ) + offsetValue;
if ( ColorChannelType.Hue == target )
{
endValue = MathX.Repeat( endValue, 360 );
}
else
{
endValue = MathX.Clamp01( endValue );
}
color = color.SetChannel( target, endValue );
return color;
}
}
}

View File

@ -0,0 +1 @@
uid://c6a2k4kldmq6k

View File

@ -0,0 +1,56 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class HSLCurves:_XX_ColorCorrectionModifier
{
[Export]
public Curve h = MathX.Curve( 0, 360, 0, 360, 0, 360 );
[Export]
public float hOffset = 0;
[Export]
public Curve s = MathX.Curve( 0, 1, 0, 1);
[Export]
public float sOffset = 0;
[Export]
public Curve l = MathX.Curve( 0, 1, 0, 1);
[Export]
public float lOffset = 0;
protected override Color _Modify( Color color )
{
var hsl = HSLColor.FromRGBA( color );
var hh = h == null ? hsl.h : h.Sample( hsl.h );
var ss = s == null ? hsl.s : s.Sample( hsl.s );
var ll = l == null ? hsl.l : l.Sample( hsl.l );
hh += hOffset;
ss += sOffset;
ll += lOffset;
hh = MathX.Repeat( hh, 360 );
ss = MathX.Clamp01( ss );
ll = MathX.Clamp01( ll );
hsl.h = hh;
hsl.s = ss;
hsl.l = ll;
return hsl.ToRGBA();
}
}
}

View File

@ -0,0 +1 @@
uid://dlxt0pm7dpuoy

View File

@ -0,0 +1,33 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class RGBCurves:_XX_ColorCorrectionModifier
{
[Export]
public Curve r;
[Export]
public Curve g;
[Export]
public Curve b;
protected override Color _Modify( Color color )
{
var rr = r == null ? color.R : r.Sample( color.R );
var gg = g == null ? color.G : g.Sample( color.G );
var bb = b == null ? color.B : b.Sample( color.B );
return new Color( rr, gg, bb, color.A );
}
}
}

View File

@ -0,0 +1 @@
uid://bf2q4pgpi2vl2

View File

@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class _XX_ColorCorrectionModifier:Resource
{
[Export(PropertyHint.Range, "0,100 suffix:%")]
public float amount = 100;
public Color Apply( Color color )
{
var modified = _Modify( color );
return ColorX.Lerp( color, modified, amount / 100f );
}
protected virtual Color _Modify( Color color )
{
return color;
}
}
}

View File

@ -0,0 +1 @@
uid://b6uqqar4kc4am

View File

@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ImageSaveSettings:Resource
{
public virtual void Save( Image image, string path )
{
}
}
}

View File

@ -0,0 +1 @@
uid://ca0t2btbm43ax

View File

@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
public class Images
{
public void Save( Image img, string path, ImageSaveSettings saveSettings )
{
saveSettings.Save( img, path );
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class PNGSettings:ImageSaveSettings
{
public override void Save( Image image, string path )
{
path = path.EnsureEndsWith( ".png" );
image.SavePng( path );
}
}
}

View File

@ -0,0 +1 @@
uid://m1agofoxqejh

View File

@ -56,11 +56,19 @@ namespace Rokojori
public static TextureCombinerBuffer From( Texture2D texture, int srgbConversion = 0 )
{
var buffer = Create( texture.GetWidth(), texture.GetHeight() );
buffer.CopyFrom( texture.GetImage(), 0, 0, 0, 0, texture.GetWidth(), texture.GetHeight(), srgbConversion);
buffer.CopyFrom( texture.GetImage(), 0, 0, 0, 0, texture.GetWidth(), texture.GetHeight(), srgbConversion );
return buffer;
}
public static TextureCombinerBuffer FromPath( string path, int srgbConversion = 0 )
{
var texture = ResourceLoader.Load<Texture2D>( path );
var sourceBuffer = TextureCombinerBuffer.From( texture, srgbConversion );
return sourceBuffer;
}
public void CopyFromSampled( TextureCombinerBuffer source, int x, int y, int w, int h )
{
// RJLog.Log( "Copy Sampled", x,y, w, h );
@ -147,6 +155,26 @@ namespace Rokojori
return ImageTexture.CreateFromImage( image );
}
public ImageTexture CreateImageTexture3D( int depth, bool alpha = true, bool generateMipmaps = false )
{
var image = Image.CreateEmpty( width, height, generateMipmaps, alpha ? Image.Format.Rgba8 : Image.Format.Rgb8);
CopyTo( 0, 0, 0, 0, width, height, image, alpha );
if ( generateMipmaps )
{
image.GenerateMipmaps();
}
return ImageTexture.CreateFromImage( image );
}
public void UpdateTexture( ImageTexture imageTexture, bool hasAlpha = true )
{
var image = imageTexture.GetImage();
CopyTo( 0, 0, 0, 0, width, height, image, hasAlpha );
imageTexture.Update( image );
}
public TextureCombinerBuffer Resize( int w, int h )
{
var buffer = Create( w, h );
@ -170,19 +198,34 @@ namespace Rokojori
{
if ( srgbConversion != 0 )
{
var gamma = Mathf.Pow( 2.2f, srgbConversion );
if ( srgbConversion == 1 )
{
for ( int i = 0; i < w; i++ )
{
for ( int j = 0; j < h; j++ )
{
var sourcePixel = image == null ? new Color( 0, 0, 0 ) : image.GetPixel( sourceX + i, sourceY + j );
sourcePixel = sourcePixel.Gamma( gamma );
sourcePixel = sourcePixel.SRGBtoLinear();
SetAt( ownX + i, ownY + j, sourcePixel );
}
}
}
else if ( srgbConversion == -1 )
{
for ( int i = 0; i < w; i++ )
{
for ( int j = 0; j < h; j++ )
{
var sourcePixel = image == null ? new Color( 0, 0, 0 ) : image.GetPixel( sourceX + i, sourceY + j );
sourcePixel = sourcePixel.LinearToSRGB();
SetAt( ownX + i, ownY + j, sourcePixel );
}
}
}
}
else
{

View File

@ -85,7 +85,7 @@ namespace Rokojori.Reallusion
if ( sRGB )
{
color = color.linearToSRGB();
color = color.LinearToSRGB();
}
return color;

28
Runtime/Text/Text.cs Normal file
View File

@ -0,0 +1,28 @@
using System.Collections;
using System.Collections.Generic;
namespace Rokojori
{
public static class Text
{
public static string EscapeAsPathForCommandLine( this string path )
{
var escaped = path.Replace( "\"", "\\\"" );
return $"\"{escaped}\"";
}
public static string EnsureEndsWith( this string path, string ending )
{
if ( ! path.EndsWith( ending ) )
{
path += ending;
}
return path;
}
}
}

View File

@ -14,6 +14,10 @@ altHold = 2
shiftHold = 2
modifiersMode = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_75e6s"]
script = ExtResource("1_ikub8")
@ -23,17 +27,29 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_gmj7t"]
script = ExtResource("3_1wc0w")
axis = 1
type = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_687xx"]
script = ExtResource("4_wj7li")
button = 12
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_unhue"]
script = ExtResource("1_ikub8")
@ -43,6 +59,10 @@ numVisible = 1
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_ikub8")
@ -52,3 +72,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -13,6 +13,10 @@ altHold = 2
shiftHold = 2
modifiersMode = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_jvxvp"]
script = ExtResource("1_e8w3r")
@ -22,11 +26,19 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_3ovg5"]
script = ExtResource("3_pyik4")
button = 10
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_yhdev"]
script = ExtResource("1_e8w3r")
@ -36,6 +48,10 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_e8w3r")
@ -45,3 +61,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -14,6 +14,10 @@ altHold = 2
shiftHold = 2
modifiersMode = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_nj1ud"]
script = ExtResource("1_r4ul7")
@ -23,17 +27,29 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_mxixb"]
script = ExtResource("3_nh2m3")
axis = 1
type = 1
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_c1vyq"]
script = ExtResource("3_dyhbp")
button = 11
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_vhtjx"]
script = ExtResource("1_r4ul7")
@ -43,6 +59,10 @@ numVisible = 1
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_r4ul7")
@ -52,3 +72,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -14,6 +14,10 @@ altHold = 2
shiftHold = 2
modifiersMode = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_qts7v"]
script = ExtResource("1_w0cyl")
@ -23,17 +27,29 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_gmj7t"]
script = ExtResource("3_ushvr")
axis = 0
type = 1
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_687xx"]
script = ExtResource("4_53tr6")
button = 13
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_0hx40"]
script = ExtResource("1_w0cyl")
@ -43,6 +59,10 @@ numVisible = 1
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_w0cyl")
@ -52,3 +72,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -14,6 +14,10 @@ altHold = 2
shiftHold = 2
modifiersMode = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_d1suo"]
script = ExtResource("1_vdwin")
@ -23,17 +27,29 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_c7ebh"]
script = ExtResource("3_4antj")
axis = 0
type = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_md70b"]
script = ExtResource("4_ucs5r")
button = 14
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_fhaor"]
script = ExtResource("1_vdwin")
@ -43,6 +59,10 @@ numVisible = 1
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_vdwin")
@ -52,3 +72,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -13,6 +13,10 @@ altHold = 2
shiftHold = 2
modifiersMode = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_pqldp"]
script = ExtResource("1_5263n")
@ -22,11 +26,19 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_ivi7v"]
script = ExtResource("3_ejsq1")
button = 9
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[sub_resource type="Resource" id="Resource_0uxqa"]
script = ExtResource("1_5263n")
@ -36,6 +48,10 @@ numVisible = 1
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_5263n")
@ -45,3 +61,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -9,6 +9,10 @@ script = ExtResource("3_5t03r")
axis = 5
type = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_lnrk8")
@ -18,3 +22,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -9,6 +9,10 @@ script = ExtResource("3_pu70j")
axis = 4
type = 0
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5
[resource]
script = ExtResource("1_w21si")
@ -18,3 +22,7 @@ numVisible = 0
inputIcons = []
useInputIconsFromSensors = true
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -0,0 +1,13 @@
using Godot;
using System.Collections.Generic;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class AdjustmentsEffect:PostProcessVolumeEffect
{
}
}

View File

@ -0,0 +1 @@
uid://b7t25777r8v13

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

View File

@ -0,0 +1,26 @@
[remap]
importer="3d_texture"
type="CompressedTexture3D"
uid="uid://dlr7kk6et1g3v"
path="res://.godot/imported/environment_adjustments_3d_lut_template.webp-6d0d1fb6bd1425e1eff35b812ef39d39.ctex3d"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/rokojori_action_library/Runtime/VirtualCameras/PostProcess/environment_adjustments_3d_lut_template.webp"
dest_files=["res://.godot/imported/environment_adjustments_3d_lut_template.webp-6d0d1fb6bd1425e1eff35b812ef39d39.ctex3d"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
slices/horizontal=33
slices/vertical=1

119
Tools/blender/Blender.cs Normal file
View File

@ -0,0 +1,119 @@
#if TOOLS
using Godot;
using Rokojori;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Rokojori.Tools
{
public class BlenderResponse
{
public int exitCode;
public string rawFileName;
public string rawArguments;
public string rawResponse;
}
public class Blender
{
static string _path = "blender" ;
public static string path
{
get => _path;
set
{
_path = value;
}
}
public static async Task<BlenderResponse> RunPython( string pythonPath, List<string> args, string workingDirectory = null )
{
var pythonArgs = new List<string>();
pythonArgs.Add( "--background " );
pythonArgs.Add( "--python " + pythonPath + " ");
pythonArgs.Add( "-- " );
pythonArgs.Add( args.Join( " " ) );
var response = await Run( pythonArgs, workingDirectory );
return response;
}
public static async Task<BlenderResponse> Run( List<string> args, string workingDirectory = null )
{
var response = new BlenderResponse();
var joinedArgs = args.Join( " ");
response.rawArguments = joinedArgs;
response.rawFileName = _path;
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = _path,
Arguments = joinedArgs,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
if ( workingDirectory != null )
{
process.StartInfo.WorkingDirectory = workingDirectory;
}
process.Start();
var outputResult = new List<string>();
var errorResult = new List<string>();
process.OutputDataReceived += (sender, e) =>
{
RJLog.Log( e.Data );
outputResult.Add( e.Data );
};
process.ErrorDataReceived += (sender, e) =>
{
RJLog.Error( e.Data );
errorResult.Add( e.Data );
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// var outputTask = process.StandardOutput.ReadToEndAsync();
// var errorTask = process.StandardError.ReadToEndAsync();
// await Task.WhenAll( outputTask, errorTask );
await process.WaitForExitAsync();
response.exitCode = process.ExitCode;
if ( process.ExitCode == 0 )
{
response.rawResponse = outputResult.Join( "" );
}
else
{
response.rawResponse = errorResult.Join( "" );
}
return response;
}
}
}
#endif

View File

@ -0,0 +1,95 @@
#if TOOLS
using Godot;
using Rokojori;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Rokojori.Tools
{
// https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.fbx
public class BlenderFBXExportSettings
{
public string path_mode = "AUTO";
public string batch_mode = "OFF";
public bool embed_textures = true;
public bool use_batch_own_dir = false;
public bool use_metadata = true;
public bool check_existing = false;
// INCLUDE
public bool use_selection = false;
public bool use_visible = false;
public bool use_active_collection = false;
public List<string> object_types = new List<string> { "EMPTY","CAMERA","LIGHT","ARMATURE","MESH","OTHER" };
public bool use_custom_props = false;
// TRANSFORM
public float global_scale = 1.0f;
public string apply_scale_options = "FBX_SCALE_ALL"; // 'FBX_SCALE_NONE', 'FBX_SCALE_UNITS', 'FBX_SCALE_CUSTOM', 'FBX_SCALE_ALL'
public string axis_forward = "-Z";
public string axis_up = "Y";
public bool apply_unit_scale = false;
public bool use_space_transform = true;
public bool bake_space_transform = true;
// GEOMETRY
public bool use_mesh_modifiers = true; // Apply Modifiers
public bool use_mesh_modifiers_render = false;
public string mesh_smooth_type = "OFF"; // OFF, FACE, EDGES
public bool use_subsurf = false;
public bool use_mesh_edges = false;
public bool use_tspace = false;
public bool use_triangles = false;
public string colors_type = "SRGB"; // NONE, SRGB, LINEAR
// ARMATURE
public bool add_leaf_bones = false;
public string primary_bone_axis = "Y";
public string secondary_bone_axis = "X";
public string armature_nodetype = "NULL";
// BAKE ANIMATION
public bool bake_anim = true;
public bool bake_anim_use_all_bones = true;
public bool bake_anim_use_nla_strips = true;
public bool bake_anim_use_all_actions = true;
public bool bake_anim_force_startend_keying = true;
public float bake_anim_step = 1.0f;
public float bake_anim_simplify_factor = 1.0f;
}
public class BlenderGLTFtoFBX
{
public static async Task<BlenderResponse> Run( string gltfPath, string fbxPath, BlenderFBXExportSettings fbxSettings = null )
{
var pythonPath = RokojoriPlugin.GlobalizedPath( "Tools/blender/gltf_to_fbx.py" );
var tempSettings = fbxSettings == null ? new BlenderFBXExportSettings() : fbxSettings;
var tempSettingsPath = RokojoriPlugin.SaveTemporaryJSON( tempSettings );
var conversionArgs = new List<string>();
conversionArgs.Add( gltfPath.EscapeAsPathForCommandLine() );
conversionArgs.Add( fbxPath.EscapeAsPathForCommandLine() );
conversionArgs.Add( tempSettingsPath.EscapeAsPathForCommandLine() );
var response = await Blender.RunPython( pythonPath, conversionArgs );
FilesSync.Delete( tempSettingsPath );
return response;
}
}
}
#endif

View File

@ -0,0 +1,47 @@
#if TOOLS
using Godot;
using Rokojori;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Rokojori.Tools
{
[Tool]
[GlobalClass]
public partial class BlenderTest:Node
{
[Export]
public string gltfPath;
[Export]
public string fbxPath;
[ExportToolButton("Convert GLTF to FBX")]
public Callable StatusButton => Callable.From(
()=>
{
Convert();
}
);
async void Convert()
{
Blender.path = "C:/Program Files/Blender Foundation/Blender 4.0/blender.exe";
this.LogInfo( "Starting conversion..." );
var globalGLTFPath = ProjectSettings.GlobalizePath( gltfPath );
var globalFBXPath = ProjectSettings.GlobalizePath( fbxPath );
var settings = new BlenderFBXExportSettings();
var response = await BlenderGLTFtoFBX.Run( globalGLTFPath, globalFBXPath, settings );
this.LogInfo( response.exitCode, ">>", response.rawResponse );
}
}
}
#endif

View File

@ -0,0 +1 @@
uid://ij6cnc0jiygn

View File

@ -0,0 +1,36 @@
import bpy
import sys
import json
# Parse CLI arguments
argv = sys.argv
input_path = argv[-3]
output_path = argv[-2]
settings_json_path = argv[-1]
print( argv )
print( input_path )
print( output_path )
print( settings_json_path )
# Reset the scene
bpy.ops.wm.read_factory_settings(use_empty=True)
# Import GLTF/GLB
bpy.ops.import_scene.gltf(filepath=input_path)
# Load FBX export settings from JSON file
with open(settings_json_path, 'r') as f:
export_settings = json.load(f)
# Convert certain keys to sets (JSON only allows lists)
for key in ['object_types']:
if key in export_settings and isinstance(export_settings[key], list):
export_settings[key] = set(export_settings[key])
print( export_settings )
# Export to FBX using settings from JSON
bpy.ops.export_scene.fbx(
filepath=output_path,
**export_settings
)

View File

@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
#if TOOLS
namespace Rokojori.Tools
{
public class GLTFExport
{
public static void Save( Node3D node, string path, string temporaryPath = "res://--temporary--" )
{
var state = new GltfState();
var doc = new GltfDocument();
doc.AppendFromScene( node, state );
doc.WriteToFilesystem( state, path );
return;
}
}
}
#endif

View File

@ -0,0 +1 @@
uid://ctvcbkmnyhgvn