Added Compositor Effects
This commit is contained in:
parent
22530dff10
commit
50e4bf74b4
|
@ -10,8 +10,14 @@ namespace Rokojori
|
|||
{
|
||||
GizmoDrawerPlugin gizmoDrawerPlugin = new GizmoDrawerPlugin();
|
||||
|
||||
public static readonly string path = "res://addons/rokojori_action_library";
|
||||
public static string Path( string path )
|
||||
{
|
||||
return RokojoriPlugin.path + "/" + path;
|
||||
}
|
||||
|
||||
static readonly string RokojoriRootAutoLoad = "RokojoriRootAutoLoad";
|
||||
static readonly string RokojoriRootAutoLoadPath = "res://addons/rokojori_action_library/Runtime/Godot/Root.cs";
|
||||
static readonly string RokojoriRootAutoLoadPath = Path( "Runtime/Godot/Root.cs" );
|
||||
|
||||
public override void _EnablePlugin()
|
||||
{
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
using Godot;
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
@ -26,5 +27,19 @@ namespace Rokojori
|
|||
var mask = 1 << position;
|
||||
return ( mask & value ) == mask ;
|
||||
}
|
||||
|
||||
public static List<byte> Convert( List<float> floats )
|
||||
{
|
||||
var list = new List<byte>();
|
||||
floats.ForEach( f => list.AddRange( BitConverter.GetBytes( f ) ) );
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<byte> Convert( List<int> ints )
|
||||
{
|
||||
var list = new List<byte>();
|
||||
ints.ForEach( i => list.AddRange( BitConverter.GetBytes( i ) ) );
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,15 @@ namespace Rokojori
|
|||
{
|
||||
public static class ColorX
|
||||
{
|
||||
public static Color UnblendBlack( this Color c, float treshold = 0 )
|
||||
{
|
||||
if ( c.A <= treshold )
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
return new Color( c.R / c.A, c.G / c.A, c.B / c.A, c.A );
|
||||
}
|
||||
|
||||
public static float AlphaMultipliedR( this Color c )
|
||||
{
|
||||
|
|
|
@ -284,6 +284,16 @@ namespace Rokojori
|
|||
RJLog.Log( resource, messages );
|
||||
}
|
||||
|
||||
public static void LogInfoIf( this Resource resource, bool condition, params object[] messages )
|
||||
{
|
||||
if ( ! condition )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RJLog.Log( resource, messages );
|
||||
}
|
||||
|
||||
public static void LogError( this Resource resource, params object[] messages )
|
||||
{
|
||||
RJLog.Error( resource, messages );
|
||||
|
|
|
@ -307,10 +307,11 @@ namespace Rokojori
|
|||
|
||||
this.LogInfo( "Texture created:", bakingMaterialModes[ i ] );
|
||||
|
||||
texture = Textures.Copy( texture );
|
||||
await this.RequestNextFrame();
|
||||
await this.RequestNextFrame();
|
||||
|
||||
texture = await CPUTextureDilater.Dilate( texture, this.RequestNextFrame );
|
||||
|
||||
|
||||
this.LogInfo( "Assigning Texture", bakingMaterialModes[ i ] );
|
||||
bakeMode.AssignMaterial( bakingMaterialModes[ i ], texture );
|
||||
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System.Linq;
|
||||
using TriangleNet.Geometry;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CPUTextureDilater
|
||||
{
|
||||
public static async Task<Texture2D> Dilate( Texture2D texture, Func<Task> waiting, float alphaTreshold = 250f/255f )
|
||||
{
|
||||
var dilater = new CPUTextureDilater();
|
||||
dilater.alphaTreshold = alphaTreshold;
|
||||
dilater.waiting = waiting;
|
||||
|
||||
var t = await dilater._Dilate( texture );
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
TextureCombinerBuffer _buffer;
|
||||
Func<Task> waiting;
|
||||
|
||||
async Task<Texture2D> _Dilate( Texture2D texture )
|
||||
{
|
||||
_buffer = TextureCombinerBuffer.From( texture );
|
||||
|
||||
await _Process();
|
||||
|
||||
return _buffer.CreateImageTexture();
|
||||
}
|
||||
|
||||
public float alphaTreshold = 250f/255f;
|
||||
|
||||
HashSet<int> _processed = new HashSet<int>();
|
||||
HashSet<int> _unprocessed = new HashSet<int>();
|
||||
HashSet<int> _edges = new HashSet<int>();
|
||||
|
||||
|
||||
|
||||
async Task _Process()
|
||||
{
|
||||
_buffer.UnblendBlack();
|
||||
|
||||
GrabInitialPixels();
|
||||
|
||||
|
||||
if ( _unprocessed.Count == _buffer.numPixels )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int maxMessages = 100;
|
||||
int messageCounter = 0;
|
||||
int messageLimit = 200000;
|
||||
|
||||
int max = 1000 * 1000;
|
||||
int it = 0;
|
||||
|
||||
var random = LCG.WithSeed( 1984 );
|
||||
|
||||
while ( _edges.Count > 0 && it < max )
|
||||
{
|
||||
it++;
|
||||
|
||||
if ( messageCounter >= messageLimit )
|
||||
{
|
||||
await waiting();
|
||||
messageCounter = 0;
|
||||
RJLog.Log(
|
||||
RegexUtility._FF( 100f *_processed.Count / _buffer.numPixels ) + "%",
|
||||
"Valid: ", ( _processed.Count + _edges.Count + _unprocessed.Count ) == _buffer.numPixels,
|
||||
"P", _processed.Count, "E",_edges.Count, "U", _unprocessed.Count
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageCounter ++;
|
||||
}
|
||||
|
||||
var edge = _edges.ElementAt( random.IntegerExclusive( _edges.Count ) );
|
||||
_edges.Remove( edge );
|
||||
|
||||
if ( ! _processed.Contains( edge ) )
|
||||
{
|
||||
var edgeColor = ComputeColor( edge );
|
||||
_processed.Add( edge );
|
||||
_buffer.SetIndexed( edge, edgeColor );
|
||||
}
|
||||
|
||||
AddEdgeNeighbors( edge );
|
||||
|
||||
}
|
||||
|
||||
if ( _edges.Count > 0 && it == max )
|
||||
{
|
||||
RJLog.Log( "Aborted, too many entries" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GrabInitialPixels()
|
||||
{
|
||||
for ( int i = 0; i < _buffer.numPixels; i++ )
|
||||
{
|
||||
if ( ! HasColor( i ) )
|
||||
{
|
||||
_unprocessed.Add( i );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsEdge( i ) )
|
||||
{
|
||||
_edges.Add( i );
|
||||
}
|
||||
|
||||
_processed.Add( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddEdgeNeighbors( int i )
|
||||
{
|
||||
var position = _buffer.ComputePositionFromIndex( i );
|
||||
|
||||
for ( int x = -1; x <= 1; x++ )
|
||||
{
|
||||
for ( int y = -1; y <= 1; y++ )
|
||||
{
|
||||
if ( x == 0 && y == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var index = _buffer.ComputeIndexFromPosition( position.X + x, position.Y + y );
|
||||
|
||||
if ( index < 0 || index >= _buffer.numPixels )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! _unprocessed.Contains( index ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_unprocessed.Remove( index );
|
||||
_edges.Add( index );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool HasColor( int x, int y )
|
||||
{
|
||||
return HasColor( _buffer.ComputeIndexFromPosition( x, y ) );
|
||||
}
|
||||
|
||||
bool HasColor( int i )
|
||||
{
|
||||
if ( i < 0 || i >= _buffer.numPixels )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var pixel = _buffer.GetIndexed( i );
|
||||
|
||||
if ( pixel.A > alphaTreshold )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _processed.Contains( i );
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool IsEdge( int i )
|
||||
{
|
||||
if ( ! HasColor( i ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var position = _buffer.ComputePositionFromIndex( i );
|
||||
|
||||
for ( int x = -1; x <= 1; x++ )
|
||||
{
|
||||
for ( int y = -1; y <= 1; y++ )
|
||||
{
|
||||
if ( x == 0 && y == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! HasColor( position.X + x, position.Y + y ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
Color ComputeColor( int i )
|
||||
{
|
||||
var position = _buffer.ComputePositionFromIndex( i );
|
||||
|
||||
var pixel = _buffer.GetIndexed( i );
|
||||
var distance = 10f;
|
||||
var closestPixel = -1;
|
||||
|
||||
for ( int x = -1; x <= 1; x++ )
|
||||
{
|
||||
for ( int y = -1; y <= 1; y++ )
|
||||
{
|
||||
if ( ( x == 0 && y == 0 ) || ! HasColor( position.X + x, position.Y + y ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var d = new Vector2( x, y ).Length();
|
||||
|
||||
if ( d >= distance )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
distance = d;
|
||||
closestPixel = _buffer.ComputeIndexFromPosition( position.X + x, position.Y + y );
|
||||
|
||||
// var pixelColor = _buffer.GetAt( position.X + x, position.Y + y );
|
||||
// rawColor += new Vector3( pixelColor.R, pixelColor.G, pixelColor.B);
|
||||
// numColors ++;
|
||||
}
|
||||
}
|
||||
|
||||
// rawColor /= numColors;
|
||||
|
||||
var rawColor = _buffer.GetIndexed( closestPixel );
|
||||
|
||||
|
||||
|
||||
return new Color( rawColor.R, rawColor.G, rawColor.B, pixel.A );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://jk67aba6i3mx
|
|
@ -17,6 +17,8 @@ namespace Rokojori
|
|||
|
||||
Color[] _pixels;
|
||||
|
||||
public int numPixels => _pixels.Length;
|
||||
|
||||
public static TextureCombinerBuffer Create( int w, int h )
|
||||
{
|
||||
var tb = new TextureCombinerBuffer();
|
||||
|
@ -27,6 +29,14 @@ namespace Rokojori
|
|||
return tb;
|
||||
}
|
||||
|
||||
public void UnblendBlack( float treshold = 10f/255f )
|
||||
{
|
||||
for ( int i = 0; i < _pixels.Length; i++ )
|
||||
{
|
||||
_pixels[ i ] = _pixels[ i ].UnblendBlack( treshold );
|
||||
}
|
||||
}
|
||||
|
||||
public static TextureCombinerBuffer From( Texture2D texture )
|
||||
{
|
||||
var buffer = Create( texture.GetWidth(), texture.GetHeight() );
|
||||
|
@ -35,6 +45,15 @@ namespace Rokojori
|
|||
return buffer;
|
||||
}
|
||||
|
||||
public ImageTexture CreateImageTexture()
|
||||
{
|
||||
var image = Image.CreateEmpty( width, height, true, Image.Format.Rgba8 );
|
||||
CopyTo( 0, 0, 0, 0, width, height, image );
|
||||
image.GenerateMipmaps();
|
||||
|
||||
return ImageTexture.CreateFromImage( image );
|
||||
}
|
||||
|
||||
public TextureCombinerBuffer Resize( int w, int h )
|
||||
{
|
||||
var buffer = Create( w, h );
|
||||
|
@ -102,6 +121,14 @@ namespace Rokojori
|
|||
return x + y * _width;
|
||||
}
|
||||
|
||||
public Vector2I ComputePositionFromIndex( int index )
|
||||
{
|
||||
var x = index % width;
|
||||
var y = index / width;
|
||||
|
||||
return new Vector2I( x, y );
|
||||
}
|
||||
|
||||
public void SetAt( int x, int y, Color value )
|
||||
{
|
||||
SetIndexed( ComputeIndexFromPosition( x, y ), value );
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class BlurCompositorEffect:CompositorEffect
|
||||
{
|
||||
public enum BlurType
|
||||
{
|
||||
Box,
|
||||
Gaussian
|
||||
}
|
||||
|
||||
[ExportGroup( "Blur Properties" )]
|
||||
[Export]
|
||||
public BlurType blurType = BlurType.Box;
|
||||
|
||||
[Export( PropertyHint.Range, "1,80" )]
|
||||
public int blurSamples = 15;
|
||||
[Export( PropertyHint.Range, "1,80" )]
|
||||
public int blurWidth = 80;
|
||||
[Export( PropertyHint.Range, "0,0.5" )]
|
||||
public float dither = 0;
|
||||
[Export( PropertyHint.Range, "1,4" )]
|
||||
public int mipLevel = 1;
|
||||
[Export]
|
||||
public bool logDebugInfo = false;
|
||||
|
||||
//for sensing when the backbuffers need rebuilding
|
||||
public Vector2I sizeCache = new Vector2I();
|
||||
public int mipCache;
|
||||
|
||||
public RenderingDevice rd;
|
||||
public Rid shader;
|
||||
public Rid pipeline;
|
||||
public Array backbuffers = new Array();
|
||||
public RDTextureFormat backbufferFormat;
|
||||
public RDTextureView texview;
|
||||
public RDSamplerState samplerState;
|
||||
public Rid linearSampler;
|
||||
|
||||
|
||||
public BlurCompositorEffect()
|
||||
{
|
||||
RJLog.Log( "_Init" );
|
||||
RenderingServer.CallOnRenderThread( Callable.From( InitializeComputeShader ) );
|
||||
}
|
||||
|
||||
public void InitializeComputeShader()
|
||||
{
|
||||
RJLog.Log( "InitializeComputeShader" );
|
||||
|
||||
rd = RenderingServer.GetRenderingDevice();
|
||||
|
||||
if ( rd == null )
|
||||
{
|
||||
RJLog.Log( "Initializing failed" );
|
||||
return;
|
||||
}
|
||||
|
||||
RJLog.Log( "Initializing succeed, loading shader" );
|
||||
//Make sure this is correctly pointing to the GLSL file
|
||||
RDShaderFile glslFile = ( RDShaderFile ) GD.Load( "res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/Blur/BlurEffect.glsl" );
|
||||
|
||||
shader = rd.ShaderCreateFromSpirV( glslFile.GetSpirV() );
|
||||
pipeline = rd.ComputePipelineCreate( shader );
|
||||
|
||||
samplerState = new RDSamplerState();
|
||||
samplerState.MinFilter = RenderingDevice.SamplerFilter.Linear;
|
||||
samplerState.MagFilter = RenderingDevice.SamplerFilter.Linear;
|
||||
|
||||
linearSampler = rd.SamplerCreate( samplerState );
|
||||
|
||||
|
||||
RJLog.Log( "Initializing done", shader, pipeline, samplerState, linearSampler );
|
||||
|
||||
}
|
||||
|
||||
public override void _Notification( int what )
|
||||
{
|
||||
if ( what != NotificationPredelete || ! shader.IsValid || rd == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rd.FreeRid( shader );
|
||||
rd.FreeRid( linearSampler );
|
||||
|
||||
foreach( var b in backbuffers )
|
||||
{
|
||||
rd.FreeRid( ( Rid ) b );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override void _RenderCallback( int effectCallbackType, RenderData renderData )
|
||||
{
|
||||
DoPass( renderData, 1 ); // horizontal blur
|
||||
DoPass( renderData, 2 ); // vertical blur
|
||||
DoPass( renderData, 3 ); // draw buffers to screen
|
||||
|
||||
}
|
||||
|
||||
public void DoPass( RenderData renderData, int passNum )
|
||||
{
|
||||
if ( rd == null )
|
||||
{
|
||||
this.LogInfoIf( logDebugInfo, "No RD" );
|
||||
return;
|
||||
}
|
||||
|
||||
//get fresh scene buffers && data for this pass
|
||||
RenderSceneBuffersRD sceneBuffers = ( RenderSceneBuffersRD ) renderData.GetRenderSceneBuffers();
|
||||
RenderSceneDataRD sceneData = ( RenderSceneDataRD ) renderData.GetRenderSceneData();
|
||||
|
||||
if ( sceneBuffers == null && sceneData == null )
|
||||
{
|
||||
this.LogInfoIf( logDebugInfo, "sceneBuffers == null && sceneData == null" );
|
||||
return;
|
||||
}
|
||||
|
||||
var size = sceneBuffers.GetInternalSize();
|
||||
|
||||
if ( size.X == 0 || size.Y == 0 )
|
||||
{
|
||||
this.LogInfoIf( logDebugInfo, "size.X == 0 || size.Y == 0" );
|
||||
return;
|
||||
}
|
||||
|
||||
int xGroups;
|
||||
int yGroups;
|
||||
|
||||
if ( passNum == 1 || passNum == 2 )
|
||||
{
|
||||
xGroups = ( size.X/mipLevel ) / 16 + 1;
|
||||
yGroups = ( size.Y/mipLevel ) / 16 + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
xGroups = size.X / 16 + 1;
|
||||
yGroups = size.Y / 16 + 1;
|
||||
|
||||
}
|
||||
|
||||
int viewCount = ( int ) sceneBuffers.GetViewCount();
|
||||
|
||||
if ( backbuffers.Count < viewCount * 2 || sizeCache != size || mipCache != mipLevel )
|
||||
{
|
||||
InitBackbuffer( viewCount * 2, size );
|
||||
}
|
||||
|
||||
var packedBytes = new List<byte>();
|
||||
var packedFloats = new List<float>();
|
||||
var packedInts = new List<int>();
|
||||
|
||||
if ( passNum == 1 || passNum == 2 )
|
||||
{
|
||||
packedFloats.Add( size.X/mipLevel ) ;
|
||||
packedFloats.Add( size.Y/mipLevel ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
packedFloats.Add( size.X ) ;
|
||||
packedFloats.Add( size.Y ) ;
|
||||
}
|
||||
|
||||
packedFloats.Add( dither ) ;
|
||||
|
||||
packedInts.Add( blurType == BlurType.Gaussian ? 1 : 0 );
|
||||
packedInts.Add( Mathf.Min( blurSamples, blurWidth )) ;
|
||||
packedInts.Add( blurWidth );
|
||||
packedInts.Add( passNum );
|
||||
|
||||
packedBytes.AddRange( Bytes.Convert( packedFloats ) );
|
||||
packedBytes.AddRange( Bytes.Convert( packedInts ) );
|
||||
|
||||
while ( packedBytes.Count < 32 )
|
||||
{
|
||||
packedBytes.Add( 0 );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < viewCount; i++ )
|
||||
{
|
||||
var view = i;
|
||||
|
||||
Rid screenTex = sceneBuffers.GetColorLayer( (uint)view );
|
||||
|
||||
Rid screenImageUniformSet = new Rid();
|
||||
Rid backbufferUniformSet1 = new Rid();
|
||||
Rid backbufferUniformSet2 = new Rid();
|
||||
|
||||
if ( passNum == 1 )
|
||||
{
|
||||
backbufferUniformSet1 = CreateImageUniformSet( (Rid) backbuffers[view] );
|
||||
screenImageUniformSet = CreateSamplerUniformSet( screenTex );
|
||||
}
|
||||
else if ( passNum == 2 )
|
||||
{
|
||||
backbufferUniformSet2 = CreateImageUniformSet( (Rid) backbuffers[ viewCount + view ] );
|
||||
backbufferUniformSet1 = CreateSamplerUniformSet( (Rid) backbuffers[view] );
|
||||
}
|
||||
else if ( passNum == 3 )
|
||||
{
|
||||
backbufferUniformSet2 = CreateImageUniformSet( screenTex );
|
||||
screenImageUniformSet = CreateSamplerUniformSet( (Rid) backbuffers[viewCount + view] );
|
||||
}
|
||||
|
||||
int computeList = (int) rd.ComputeListBegin();
|
||||
rd.ComputeListBindComputePipeline( computeList, pipeline );
|
||||
|
||||
if ( passNum == 1 )
|
||||
{
|
||||
rd.ComputeListBindUniformSet( computeList, backbufferUniformSet1, 0 );
|
||||
rd.ComputeListBindUniformSet( computeList, screenImageUniformSet, 1 );
|
||||
}
|
||||
else if ( passNum == 2 )
|
||||
{
|
||||
rd.ComputeListBindUniformSet( computeList, backbufferUniformSet2, 0 );
|
||||
rd.ComputeListBindUniformSet( computeList, backbufferUniformSet1, 1 );
|
||||
}
|
||||
else if ( passNum == 3 )
|
||||
{
|
||||
rd.ComputeListBindUniformSet( computeList, screenImageUniformSet, 1 );
|
||||
rd.ComputeListBindUniformSet( computeList, backbufferUniformSet2, 0 );
|
||||
|
||||
}
|
||||
|
||||
rd.ComputeListSetPushConstant( computeList, packedBytes.ToArray(), (uint) packedBytes.Count );
|
||||
rd.ComputeListDispatch( computeList, (uint) xGroups, (uint)yGroups, 1 );
|
||||
rd.ComputeListEnd();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Rid CreateImageUniformSet( Rid image )
|
||||
{
|
||||
var uniform = new RDUniform();
|
||||
uniform.UniformType = RenderingDevice.UniformType.Image;
|
||||
uniform.Binding = 0;
|
||||
uniform.AddId( image );
|
||||
return UniformSetCacheRD.GetCache( shader, 0, new Array<RDUniform>(){uniform} );
|
||||
}
|
||||
|
||||
Rid CreateSamplerUniformSet( Rid texture )
|
||||
{
|
||||
var uniform = new RDUniform();
|
||||
uniform.UniformType = RenderingDevice.UniformType.SamplerWithTexture;
|
||||
uniform.Binding = 0;
|
||||
uniform.AddId( linearSampler );
|
||||
uniform.AddId( texture ) ;
|
||||
return UniformSetCacheRD.GetCache( shader, 1, new Array<RDUniform>(){uniform} );
|
||||
}
|
||||
|
||||
|
||||
public void InitBackbuffer( int count, Vector2I size )
|
||||
{
|
||||
//remember to properly free the buffers else the memory leak will blow up your pc
|
||||
foreach( var b in backbuffers )
|
||||
{
|
||||
rd.FreeRid( ( Rid ) b );
|
||||
}
|
||||
|
||||
backbuffers.Clear();
|
||||
|
||||
if ( backbufferFormat == null )
|
||||
{
|
||||
backbufferFormat = new RDTextureFormat();
|
||||
//there's loads of formats to choose from. This one is RGBA 16bit float with values 0.0 - 1.0
|
||||
}
|
||||
|
||||
backbufferFormat.Format = RenderingDevice.DataFormat.R16G16B16A16Unorm;
|
||||
backbufferFormat.Width = ( uint ) ( size.X / mipLevel );
|
||||
backbufferFormat.Height = ( uint ) ( size.Y / mipLevel );
|
||||
backbufferFormat.UsageBits =
|
||||
RenderingDevice.TextureUsageBits.StorageBit |
|
||||
RenderingDevice.TextureUsageBits.SamplingBit;
|
||||
|
||||
if ( texview == null )
|
||||
{
|
||||
texview = new RDTextureView();
|
||||
}
|
||||
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
backbuffers.Add( rd.TextureCreate( backbufferFormat, texview ) );
|
||||
}
|
||||
|
||||
mipCache = mipLevel;
|
||||
sizeCache.X = size.X;
|
||||
sizeCache.Y = size.Y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://dlgkbntnwnjyg
|
|
@ -0,0 +1,98 @@
|
|||
#[compute]
|
||||
#version 450
|
||||
|
||||
//process 16 by 16 chunk per invocation. Chosen arbitrarily. Im not sure what the optimal value here is
|
||||
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
|
||||
|
||||
//documentation reccomends using 'restrict' whenever possible. Not able to use it with the sampler
|
||||
layout(rgba16f, binding=0, set=0) restrict uniform image2D image1; //write
|
||||
layout(binding=0, set=1) uniform sampler2D sampler1; //read
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 screen_size; //dimensions of buffer we're writing to
|
||||
float dither; //[0.0 - 0.5] amount of dithering
|
||||
int is_gaussian; //[0 - 1] 0 is box blur, 1 is gaussian weighting
|
||||
int kern_samples; //number of samples per pass
|
||||
int kern_width; //width of the blur
|
||||
int pass; //[1,2,3] 1 horizontal blur, 2 vertical blur, 3 copy to screen
|
||||
} p;
|
||||
|
||||
// Random number generator for dithering
|
||||
float rng(vec2 seed) {
|
||||
return fract(sin(dot(seed.xy, vec2(12.9898, 78.233))) * 43758.5453123);
|
||||
}
|
||||
|
||||
// Apply dithering to the kernel offset
|
||||
float get_dither(float ks) {
|
||||
if (p.dither <= 0.0) return 0.0; //avoid some math if we don't need dithering
|
||||
return (rng(gl_GlobalInvocationID.xy / p.screen_size) * 2.0 - 1.0) * ks * p.dither;
|
||||
}
|
||||
|
||||
// Compute Gaussian weight for a given distance and sigma
|
||||
float gaussian(float x, float sigma) {
|
||||
return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * 3.14159) * sigma);
|
||||
}
|
||||
|
||||
// Precompute Gaussian weights for the kernel
|
||||
void compute_gaussian_weights(int samples, float sigma, out float weights[161]) {
|
||||
float total_weight = 0.0;
|
||||
for (int i = 0; i <= samples; i++) {
|
||||
float x = float(i) - float(samples) / 2.0;
|
||||
weights[i] = gaussian(x, sigma);
|
||||
total_weight += weights[i];
|
||||
}
|
||||
// Normalize weights
|
||||
for (int i = 0; i <= samples; i++) {
|
||||
weights[i] /= total_weight;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
//coordinates
|
||||
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec2 uv = vec2(pixel) / p.screen_size;
|
||||
|
||||
// Early exit if the pixel is outside the screen bounds
|
||||
if (pixel.x >= p.screen_size.x || pixel.y >= p.screen_size.y) return;
|
||||
|
||||
// Kernel parameters
|
||||
float kern_spacing = float(p.kern_width) / float(p.kern_samples);
|
||||
float kern_half = float(p.kern_width) * 0.5;
|
||||
|
||||
// Gaussian weights
|
||||
float weights[161]; //Not allowed to have variable length arrays in GLSL, so hard code theoretical max kernel size.
|
||||
float sigma = float(p.kern_width) / (6.0 * float(p.kern_width)/float(p.kern_samples)); // Adjust sigma based on kernel width
|
||||
if(p.is_gaussian == 1) {
|
||||
compute_gaussian_weights(p.kern_samples, sigma, weights);
|
||||
}
|
||||
|
||||
vec4 col = vec4(0.0);
|
||||
vec2 coord;
|
||||
|
||||
if (p.pass == 1) {
|
||||
// Horizontal pass
|
||||
for (int i = 0; i <= p.kern_samples; i++) {
|
||||
coord.x = pixel.x + (i * kern_spacing) - kern_half + get_dither(kern_spacing);
|
||||
coord.y = pixel.y;
|
||||
//if gaussian blur, add a weighted sample, if box we can average all at once at the end of loop
|
||||
if(p.is_gaussian == 1) col += texture(sampler1, clamp(coord / p.screen_size, 0.0, 1.0)) * weights[i];
|
||||
else col += texture(sampler1, clamp(coord / p.screen_size, 0.0, 1.0));
|
||||
}
|
||||
if(p.is_gaussian == 0) col.rgb /= float(p.kern_samples + 1);
|
||||
imageStore(image1, pixel, col);
|
||||
} else if (p.pass == 2) {
|
||||
// Vertical pass
|
||||
for (int j = 0; j <= p.kern_samples; j++) {
|
||||
coord.x = pixel.x;
|
||||
coord.y = pixel.y + (j * kern_spacing) - kern_half + get_dither(kern_spacing);
|
||||
if(p.is_gaussian == 1) col += texture(sampler1, clamp(coord / p.screen_size, 0.0, 1.0)) * weights[j];
|
||||
else col += texture(sampler1, clamp(coord / p.screen_size, 0.0, 1.0));
|
||||
}
|
||||
if(p.is_gaussian == 0) col.rgb /= float(p.kern_samples + 1);
|
||||
imageStore(image1, pixel, col);
|
||||
} else if (p.pass == 3) {
|
||||
//copy buffer to screen
|
||||
col = texture(sampler1, uv);
|
||||
imageStore(image1, pixel, col);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="glsl"
|
||||
type="RDShaderFile"
|
||||
uid="uid://b7s44e3k68axi"
|
||||
path="res://.godot/imported/BlurEffect.glsl-adc1ff46efeaf20c40c851da38f62fae.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/Blur/BlurEffect.glsl"
|
||||
dest_files=["res://.godot/imported/BlurEffect.glsl-adc1ff46efeaf20c40c851da38f62fae.res"]
|
||||
|
||||
[params]
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class DepthAntiAliasingEffect:SingleShaderCompositorEffect
|
||||
{
|
||||
public static readonly string shaderPath =
|
||||
RokojoriPlugin.Path( "Runtime/Rendering/CompositorEffects/DepthAntiAliasing/DepthAntiAliasingShader.glsl" );
|
||||
|
||||
|
||||
RDPushConstants constants = new RDPushConstants();
|
||||
RDSampler sampler;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float greyAmount;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float depthAmount;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float effectStrength = 1;
|
||||
|
||||
[Export( PropertyHint.Range, "0,20") ]
|
||||
public float depthEdgeTreshold = 2f;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float depthEdgeIntensity = 1f;
|
||||
|
||||
[Export( PropertyHint.Range, "0,100") ]
|
||||
public float colorContrastTreshold = 2f;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float colorContrastIntensity = 1f;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float debugView = 0f;
|
||||
|
||||
protected override void OnConfigure()
|
||||
{
|
||||
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
|
||||
_shaderPath = shaderPath;
|
||||
_groupSize = 8;
|
||||
}
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
base.OnInitialize();
|
||||
|
||||
sampler = Sampler( RenderingDevice.SamplerFilter.Nearest, RenderingDevice.SamplerRepeatMode.ClampToEdge );
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void ForAllViews()
|
||||
{
|
||||
constants.Set(
|
||||
(Vector2)context.internalSize,
|
||||
new Vector2( greyAmount, depthAmount ),
|
||||
effectStrength,
|
||||
depthEdgeTreshold / 100f,
|
||||
depthEdgeIntensity * 20f,
|
||||
colorContrastTreshold / 100f,
|
||||
colorContrastIntensity * 20f,
|
||||
debugView
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RenderView()
|
||||
{
|
||||
context.Assign_ScreenColorTexture();
|
||||
context.Assign_ScreenDepthTexture( sampler );
|
||||
|
||||
context.pushConstants = constants;
|
||||
|
||||
context.Render();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://cwhhaasonvhy
|
|
@ -0,0 +1,94 @@
|
|||
#[compute]
|
||||
#version 450
|
||||
|
||||
layout( local_size_x = 8, local_size_y = 8, local_size_z = 1 ) in;
|
||||
|
||||
layout( rgba16f, set = 0, binding = 0 )
|
||||
uniform image2D color_image;
|
||||
|
||||
layout( set = 1, binding = 0 )
|
||||
uniform sampler2D depth_sampler;
|
||||
|
||||
|
||||
layout( push_constant, std430 )
|
||||
uniform Params
|
||||
{
|
||||
vec2 rasterSize;
|
||||
vec2 amounts;
|
||||
float effectStrength;
|
||||
float edgeThreshold;
|
||||
float edgeIntensity;
|
||||
float contrastThreshold;
|
||||
float contrastIntensity;
|
||||
float debugView;
|
||||
|
||||
} params;
|
||||
|
||||
float sampleDepth( ivec2 coord )
|
||||
{
|
||||
coord = clamp( coord, ivec2( 0 ), ivec2( params.rasterSize ) - ivec2( 1 ) );
|
||||
|
||||
vec2 uv = ( vec2( coord ) + 0.5 ) / params.rasterSize;
|
||||
return texture( depth_sampler, uv ).r;
|
||||
}
|
||||
|
||||
void main( )
|
||||
{
|
||||
ivec2 uv = ivec2( gl_GlobalInvocationID.xy );
|
||||
ivec2 size = ivec2( params.rasterSize );
|
||||
|
||||
if ( uv.x >= size.x || uv.y >= size.y )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float centerDepth = sampleDepth( uv );
|
||||
|
||||
float top_left = sampleDepth( uv + ivec2( -1, -1 ) );
|
||||
float top = sampleDepth( uv + ivec2( 0, -1 ) );
|
||||
float top_right = sampleDepth( uv + ivec2( 1, -1 ) );
|
||||
float left = sampleDepth( uv + ivec2( -1, 0 ) );
|
||||
float right = sampleDepth( uv + ivec2( 1, 0 ) );
|
||||
float bottom_left = sampleDepth( uv + ivec2( -1, 1 ) );
|
||||
float bottom = sampleDepth( uv + ivec2( 0, 1 ) );
|
||||
float bottom_right = sampleDepth( uv + ivec2( 1, 1 ) );
|
||||
|
||||
|
||||
float gx = -top_left - 2.0 * left - bottom_left + top_right + 2.0 * right + bottom_right;
|
||||
float gy = -top_left - 2.0 * top - top_right + bottom_left + 2.0 * bottom + bottom_right;
|
||||
|
||||
float edgeStrength = length( vec2( gx, gy ) );
|
||||
|
||||
// float edgeStrength = abs( left - right );
|
||||
|
||||
edgeStrength = clamp( ( edgeStrength - params.edgeThreshold ) * params.edgeIntensity, 0.0, 1.0 );
|
||||
float edge = edgeStrength;
|
||||
|
||||
|
||||
vec4 color = imageLoad( color_image, uv );
|
||||
|
||||
vec4 color_left = imageLoad( color_image, uv + ivec2( -1, 0 ) );
|
||||
vec4 color_left2 = imageLoad( color_image, uv + ivec2( -2, 0 ) );
|
||||
vec4 color_right = imageLoad( color_image, uv + ivec2( 1, 0 ) );
|
||||
vec4 color_right2 = imageLoad( color_image, uv + ivec2( 2, 0 ) );
|
||||
|
||||
float c = ( length( color_left - color_right ) - params.contrastThreshold ) * params.contrastIntensity;
|
||||
|
||||
float d = min( 1.0, c );
|
||||
|
||||
edge = max( d, edge );
|
||||
|
||||
vec4 smoothedColor = color_left + color_left2 +
|
||||
2.0 * color +
|
||||
color_right + color_right2;
|
||||
|
||||
smoothedColor /= 6.0;
|
||||
|
||||
float gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721;
|
||||
vec3 debugViewColor = vec3( params.amounts.x * gray, params.amounts.y * centerDepth, edge );
|
||||
|
||||
color.rgb = mix( color.rgb, smoothedColor.rgb, edge * params.effectStrength );
|
||||
color.rgb = mix( color.rgb, debugViewColor, params.debugView );
|
||||
|
||||
imageStore( color_image, uv, color );
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="glsl"
|
||||
type="RDShaderFile"
|
||||
uid="uid://us4n0un8oo2v"
|
||||
path="res://.godot/imported/DepthAntiAliasingShader.glsl-ea5a9a21bccd9baeec39a54eb65d5afd.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/DepthAntiAliasing/DepthAntiAliasingShader.glsl"
|
||||
dest_files=["res://.godot/imported/DepthAntiAliasingShader.glsl-ea5a9a21bccd9baeec39a54eb65d5afd.res"]
|
||||
|
||||
[params]
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class DepthViewEffect:SingleShaderCompositorEffect
|
||||
{
|
||||
public static readonly string shaderPath =
|
||||
RokojoriPlugin.Path( "Runtime/Rendering/CompositorEffects/DepthView/DepthViewShader.glsl" );
|
||||
|
||||
|
||||
RDPushConstants constants = new RDPushConstants();
|
||||
RDSampler sampler;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float greyAmount;
|
||||
|
||||
[Export( PropertyHint.Range, "0,1") ]
|
||||
public float depthAmount;
|
||||
|
||||
[Export( PropertyHint.Range, "0.001,3") ]
|
||||
public float depthPower;
|
||||
|
||||
protected override void OnConfigure()
|
||||
{
|
||||
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
|
||||
_shaderPath = shaderPath;
|
||||
_groupSize = 8;
|
||||
}
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
base.OnInitialize();
|
||||
|
||||
sampler = Sampler( RenderingDevice.SamplerFilter.Nearest, RenderingDevice.SamplerRepeatMode.ClampToEdge );
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void ForAllViews()
|
||||
{
|
||||
constants.Set(
|
||||
(Vector2)context.internalSize,
|
||||
new Vector2( greyAmount, depthAmount ),
|
||||
depthPower
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RenderView()
|
||||
{
|
||||
context.Assign_ScreenColorTexture();
|
||||
context.Assign_ScreenDepthTexture( sampler );
|
||||
|
||||
context.pushConstants = constants;
|
||||
|
||||
context.Render();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://bxnpsjejq7ldo
|
|
@ -0,0 +1,48 @@
|
|||
#[compute]
|
||||
#version 450
|
||||
|
||||
layout( local_size_x = 8, local_size_y = 8, local_size_z = 1 ) in;
|
||||
|
||||
layout( rgba16f, set = 0, binding = 0)
|
||||
uniform image2D color_image;
|
||||
|
||||
layout( set = 1, binding = 0)
|
||||
uniform sampler2D depth_sampler;
|
||||
|
||||
|
||||
layout(push_constant, std430)
|
||||
uniform Params
|
||||
{
|
||||
vec2 raster_size;
|
||||
vec2 amounts;
|
||||
float power;
|
||||
|
||||
} params;
|
||||
|
||||
float sample_depth( ivec2 coord )
|
||||
{
|
||||
coord = clamp( coord, ivec2( 0 ), ivec2( params.raster_size ) - ivec2( 1 ) );
|
||||
|
||||
vec2 uv = ( vec2( coord ) + 0.5 ) / params.raster_size;
|
||||
return texture( depth_sampler, uv ).r;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 uv = ivec2( gl_GlobalInvocationID.xy );
|
||||
ivec2 size = ivec2( params.raster_size );
|
||||
|
||||
if ( uv.x >= size.x || uv.y >= size.y )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 color = imageLoad( color_image, uv );
|
||||
float depth = sample_depth( uv );
|
||||
|
||||
float gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721;
|
||||
|
||||
color.rgb = vec3( params.amounts.x * gray, pow( params.amounts.y * depth, params.power ), 0.0 );
|
||||
|
||||
imageStore( color_image, uv, color );
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="glsl"
|
||||
type="RDShaderFile"
|
||||
uid="uid://ccufacegh2n8s"
|
||||
path="res://.godot/imported/DepthViewShader.glsl-c19eff884d9bff6ff641a138482426b5.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/DepthView/DepthViewShader.glsl"
|
||||
dest_files=["res://.godot/imported/DepthViewShader.glsl-c19eff884d9bff6ff641a138482426b5.res"]
|
||||
|
||||
[params]
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#[compute]
|
||||
#version 450
|
||||
|
||||
layout( local_size_x = 16, local_size_y = 16, local_size_z = 1 ) in;
|
||||
|
||||
layout( rgba16f, set = 0, binding = 0)
|
||||
uniform image2D color_image;
|
||||
|
||||
layout(push_constant, std430)
|
||||
uniform Params
|
||||
{
|
||||
vec2 raster_size;
|
||||
vec2 reserved;
|
||||
|
||||
} params;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 uv = ivec2( gl_GlobalInvocationID.xy );
|
||||
ivec2 size = ivec2( params.raster_size );
|
||||
|
||||
if ( uv.x >= size.x || uv.y >= size.y )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 color = imageLoad( color_image, uv );
|
||||
|
||||
float gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721;
|
||||
|
||||
color.rgb = vec3( gray );
|
||||
|
||||
imageStore( color_image, uv, color );
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="glsl"
|
||||
type="RDShaderFile"
|
||||
uid="uid://biw2n3t4ci7t"
|
||||
path="res://.godot/imported/GrayScaleShader.glsl-89be3b50af0d77c6515222d93b33ffb3.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/GreyScale/GrayScaleShader.glsl"
|
||||
dest_files=["res://.godot/imported/GrayScaleShader.glsl-89be3b50af0d77c6515222d93b33ffb3.res"]
|
||||
|
||||
[params]
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class GreyScaleEffect:SingleShaderCompositorEffect
|
||||
{
|
||||
public static readonly string shaderPath =
|
||||
RokojoriPlugin.Path( "Runtime/Rendering/CompositorEffects/GreyScale/GrayScaleShader.glsl" );
|
||||
|
||||
|
||||
protected override void OnConfigure()
|
||||
{
|
||||
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
|
||||
_shaderPath = shaderPath;
|
||||
_groupSize = 8;
|
||||
}
|
||||
|
||||
RDPushConstants constants = new RDPushConstants();
|
||||
|
||||
protected override void ForAllViews()
|
||||
{
|
||||
constants.Set(
|
||||
(Vector2)context.internalSize,
|
||||
Vector2.Zero
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RenderView()
|
||||
{
|
||||
context.Assign_ScreenColorTexture();
|
||||
|
||||
context.pushConstants = constants;
|
||||
|
||||
context.Render();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://c81fs31jiamwd
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class PixelationEffect:SingleShaderCompositorEffect
|
||||
{
|
||||
public static readonly string shaderPath =
|
||||
RokojoriPlugin.Path( "Runtime/Rendering/CompositorEffects/Pixelation/PixelationShader.glsl" );
|
||||
|
||||
[Export]
|
||||
public float pixelSize = 1f;
|
||||
|
||||
[Export]
|
||||
public float pixelSizePower = 1f;
|
||||
|
||||
protected override void OnConfigure()
|
||||
{
|
||||
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
|
||||
_shaderPath = shaderPath;
|
||||
_groupSize = 8;
|
||||
}
|
||||
|
||||
RDPushConstants constants = new RDPushConstants();
|
||||
|
||||
protected override void ForAllViews()
|
||||
{
|
||||
constants.Set(
|
||||
(Vector2)context.internalSize,
|
||||
Mathf.Pow( pixelSize, pixelSizePower )
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RenderView()
|
||||
{
|
||||
context.Assign_ScreenColorTexture();
|
||||
|
||||
context.pushConstants = constants;
|
||||
|
||||
context.Render();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://ljinskwo4rsc
|
|
@ -0,0 +1,34 @@
|
|||
#[compute]
|
||||
#version 450
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(rgba16f, set = 0, binding = 0) uniform image2D color_image;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec2 raster_size;
|
||||
float pixel_size;
|
||||
float reserved;
|
||||
} params;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = ivec2(params.raster_size);
|
||||
|
||||
if (uv.x >= size.x || uv.y >= size.y )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float x = float(int(gl_GlobalInvocationID.x) % int(params.pixel_size));
|
||||
float y = float(int(gl_GlobalInvocationID.y) % int(params.pixel_size));
|
||||
|
||||
x = gl_GlobalInvocationID.x + floor(params.pixel_size / 2.0) - x;
|
||||
y = gl_GlobalInvocationID.y + floor(params.pixel_size / 2.0) - y;
|
||||
|
||||
x = min( x, params.raster_size.x - max( 1.0, params.pixel_size / 2.0 ) );
|
||||
y = min( y, params.raster_size.y - max( 1.0, params.pixel_size / 2.0 ) );
|
||||
|
||||
vec4 color = imageLoad(color_image, ivec2( x, y ) );
|
||||
imageStore(color_image, uv, color);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="glsl"
|
||||
type="RDShaderFile"
|
||||
uid="uid://ghbawysn1d3c"
|
||||
path="res://.godot/imported/PixelationShader.glsl-7bf34134c1434ce83105711f1252021f.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/Pixelation/PixelationShader.glsl"
|
||||
dest_files=["res://.godot/imported/PixelationShader.glsl-7bf34134c1434ce83105711f1252021f.res"]
|
||||
|
||||
[params]
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class RadialBlurEffect:SingleShaderCompositorEffect
|
||||
{
|
||||
public static readonly string shaderPath =
|
||||
RokojoriPlugin.Path( "Runtime/Rendering/CompositorEffects/RadialBlur/RadialBlurShader.glsl" );
|
||||
|
||||
[Export]
|
||||
public Vector2 center = new Vector2( 0.5f, 0.5f );
|
||||
|
||||
[Export]
|
||||
public float radius = 0.5f;
|
||||
|
||||
[Export]
|
||||
public float intensity = 0.5f;
|
||||
|
||||
[Export]
|
||||
public float samples = 16f;
|
||||
|
||||
protected override void OnConfigure()
|
||||
{
|
||||
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
|
||||
_shaderPath = shaderPath;
|
||||
_groupSize = 32;
|
||||
}
|
||||
|
||||
RDPushConstants constants = new RDPushConstants();
|
||||
|
||||
protected override void ForAllViews()
|
||||
{
|
||||
constants.Set(
|
||||
center,
|
||||
radius,
|
||||
intensity,
|
||||
samples,
|
||||
Vector2.Zero
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RenderView()
|
||||
{
|
||||
context.Assign_ScreenColorTexture();
|
||||
|
||||
context.pushConstants = constants;
|
||||
|
||||
context.Render();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://c5np4sijoledw
|
|
@ -0,0 +1,48 @@
|
|||
#[compute]
|
||||
#version 450
|
||||
|
||||
layout( local_size_x = 32, local_size_y = 32, local_size_z = 1 ) in;
|
||||
|
||||
layout( rgba16f, set = 0, binding = 0 )
|
||||
uniform image2D color_image;
|
||||
|
||||
layout( push_constant, std430 )
|
||||
uniform Params
|
||||
{
|
||||
vec2 center; // 8 bytes
|
||||
float radius; // 4 bytes
|
||||
float intensity; // 4 bytes
|
||||
float samples; // 4 bytes ( will be cast to int )
|
||||
vec2 _padding; // 8 bytes ( matches 32-byte total )
|
||||
|
||||
} params;
|
||||
|
||||
void main( )
|
||||
{
|
||||
ivec2 img_size = imageSize( color_image );
|
||||
ivec2 texel_coord = ivec2( gl_GlobalInvocationID.xy );
|
||||
|
||||
if ( any( greaterThanEqual( texel_coord, img_size ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 uv = ( vec2( texel_coord ) + 0.5 ) / vec2( img_size );
|
||||
vec2 dir = normalize( uv - params.center );
|
||||
int num_samples = int( clamp( params.samples, 1.0, 64.0 ) );
|
||||
|
||||
vec4 color = vec4( 0.0 );
|
||||
|
||||
for ( int i = 0; i < num_samples; i++ )
|
||||
{
|
||||
float t = float( i ) / float( num_samples );
|
||||
vec2 sample_uv = clamp( uv + dir * t * params.radius, vec2( 0.0 ), vec2( 1.0 ) );
|
||||
ivec2 sample_coord = ivec2( sample_uv * vec2( img_size ) );
|
||||
sample_coord = clamp( sample_coord, ivec2( 0 ), img_size - ivec2( 1 ) );
|
||||
color += imageLoad( color_image, sample_coord );
|
||||
}
|
||||
|
||||
color /= float( num_samples );
|
||||
vec4 original_color = imageLoad( color_image, texel_coord );
|
||||
imageStore( color_image, texel_coord, mix( original_color, color, params.intensity ) );
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[remap]
|
||||
|
||||
importer="glsl"
|
||||
type="RDShaderFile"
|
||||
uid="uid://yioccj34hlex"
|
||||
path="res://.godot/imported/RadialBlurShader.glsl-495ace3264af9b141d13d88646f4d37b.res"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/rokojori_action_library/Runtime/Rendering/CompositorEffects/RadialBlur/RadialBlurShader.glsl"
|
||||
dest_files=["res://.godot/imported/RadialBlurShader.glsl-495ace3264af9b141d13d88646f4d37b.res"]
|
||||
|
||||
[params]
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class TextureDilationCompositerEffect:CompositorEffect
|
||||
{
|
||||
RenderingDevice renderingDevice;
|
||||
Rid frameBuffer;
|
||||
|
||||
public void _Init()
|
||||
{
|
||||
EffectCallbackType = EffectCallbackTypeEnum.PostOpaque;
|
||||
renderingDevice = RenderingServer.GetRenderingDevice();
|
||||
|
||||
}
|
||||
|
||||
public override void _Notification(int what)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void _RenderCallback( int effectCallbackType, RenderData renderData )
|
||||
{
|
||||
if ( renderingDevice == null || effectCallbackType != (int) EffectCallbackTypeEnum.PostOpaque )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var renderBuffers = (RenderSceneBuffersRD) renderData.GetRenderSceneBuffers();
|
||||
|
||||
if ( renderBuffers == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var size = renderBuffers.GetInternalSize();
|
||||
|
||||
if ( size.X == 0 || size.Y == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var groups = new Vector3((size.X - 1.0f) / 8.0f + 1.0f, (size.Y - 1.0f) / 8.0f + 1.0f, 1.0f).Floor();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://3x8f0sys0kxb
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CompositorEffectGraphConnection:CompositorEffectGraphNode
|
||||
{
|
||||
public CompositorEffectGraphConnection( _XX_CompositorEffectGraph graph ):base( graph ){}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://d0kyta606sede
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CompositorEffectGraphInput:CompositorEffectGraphConnection
|
||||
{
|
||||
public CompositorEffectGraphInput( _XX_CompositorEffectGraph graph ):base( graph ){}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://d2eejfvpgahbr
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CompositorEffectGraphNode
|
||||
{
|
||||
_XX_CompositorEffectGraph _graph;
|
||||
public _XX_CompositorEffectGraph graph => _graph;
|
||||
|
||||
public CompositorEffectGraphNode( _XX_CompositorEffectGraph graph )
|
||||
{
|
||||
_graph = graph;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://dockxjmc1equ8
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CompositorEffectGraphOutput:CompositorEffectGraphConnection
|
||||
{
|
||||
public CompositorEffectGraphOutput( _XX_CompositorEffectGraph graph ):base( graph ){}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://c1tsn5wy1vpmx
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CompositorEffectGraphProcessor:CompositorEffectGraphNode
|
||||
{
|
||||
public CompositorEffectGraphProcessor( _XX_CompositorEffectGraph graph ):base( graph ){}
|
||||
|
||||
bool _initialized = false;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if ( _initialized )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnInitialize();
|
||||
}
|
||||
|
||||
protected virtual void OnInitialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Process()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://88e7cnyhvdba
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CompositorEffectShaderProcessor:CompositorEffectGraphProcessor
|
||||
{
|
||||
protected string _shaderPath;
|
||||
|
||||
public CompositorEffectShaderProcessor( _XX_CompositorEffectGraph graph, string shaderPath ):base( graph )
|
||||
{
|
||||
this._shaderPath = shaderPath;
|
||||
}
|
||||
|
||||
protected RDShader _shader;
|
||||
protected RDPipeline _pipeline;
|
||||
protected RDPushConstants _constants;
|
||||
protected List<RDUniformSet> _uniformSets = new List<RDUniformSet>();
|
||||
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
graph.Verbose( "Trying to load shader: ", _shaderPath );
|
||||
|
||||
if ( _shaderPath == null )
|
||||
{
|
||||
graph.Error( "_shaderPath == null" );
|
||||
return;
|
||||
}
|
||||
|
||||
var glslFile = GD.Load<RDShaderFile>( _shaderPath );
|
||||
|
||||
if ( glslFile == null )
|
||||
{
|
||||
graph.Error( "Couldn't load shader at path:", _shaderPath );
|
||||
return;
|
||||
}
|
||||
|
||||
_shader = RDShader.CreateFromSpirV( graph, glslFile.GetSpirV() );
|
||||
|
||||
if ( _shader == null )
|
||||
{
|
||||
graph.Error( "Couldn't create shader from code, path:", _shaderPath );
|
||||
return;
|
||||
}
|
||||
|
||||
_pipeline = RDPipeline.Create( graph, _shader );
|
||||
|
||||
if ( _shader == null )
|
||||
{
|
||||
graph.Error( "Couldn't create pipeline from compiled shader, path:", _shaderPath );
|
||||
return;
|
||||
}
|
||||
|
||||
graph.Verbose( "Created shader at path: ", _shaderPath );
|
||||
}
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
var context = graph.context;
|
||||
|
||||
context.Clear();
|
||||
context.SetShaderAndPipeline( _shader, _pipeline );
|
||||
|
||||
_uniformSets.ForEach( context.AddUniformSet );
|
||||
|
||||
context.pushConstants = _constants;
|
||||
|
||||
context.Render();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://cgjc4lhtxmyth
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class _XX_CompositorEffectGraph:RokojoriCompositorEffect
|
||||
{
|
||||
List<CompositorEffectGraphNode> _nodes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://b8c32jccxtncj
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDComputeList
|
||||
{
|
||||
RenderingDevice _rd;
|
||||
public RenderingDevice rd => _rd;
|
||||
|
||||
long _computeListID;
|
||||
public long id => _computeListID;
|
||||
|
||||
public RDComputeList( RenderingDevice rd, long id )
|
||||
{
|
||||
_rd = rd;
|
||||
_computeListID = id;
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
rd.ComputeListEnd();
|
||||
_computeListID = 0;
|
||||
_rd = null;
|
||||
}
|
||||
|
||||
public void BindPipeline( RDPipeline pipeline )
|
||||
{
|
||||
rd.ComputeListBindComputePipeline( _computeListID, pipeline.rid );
|
||||
}
|
||||
|
||||
public void BindUniformSet( RDUniformSet uniformSet, int index )
|
||||
{
|
||||
rd.ComputeListBindUniformSet( _computeListID, uniformSet.rid, (uint) index );
|
||||
}
|
||||
|
||||
public void SetPushConstants( RDPushConstants constants )
|
||||
{
|
||||
var bytes = constants.bytes;
|
||||
rd.ComputeListSetPushConstant( _computeListID, bytes, (uint) bytes.Length );
|
||||
}
|
||||
|
||||
public void Dispatch( Vector3I groups )
|
||||
{
|
||||
rd.ComputeListDispatch( _computeListID, (uint)groups.X, (uint)groups.Y, (uint)groups.Z );
|
||||
}
|
||||
|
||||
public static RDComputeList Begin( RenderingDevice rd )
|
||||
{
|
||||
return new RDComputeList( rd, rd.ComputeListBegin() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://cq0h2frxmmtx0
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDPipeline: RenderingObject
|
||||
{
|
||||
public RDPipeline( RokojoriCompositorEffect effect, Rid rid ):base( effect, rid )
|
||||
{}
|
||||
|
||||
public static RDPipeline Create( RokojoriCompositorEffect effect, RDShader shader )
|
||||
{
|
||||
return new RDPipeline( effect, effect.rd.ComputePipelineCreate( shader.rid ) );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://de7bto3duqn2i
|
|
@ -0,0 +1,230 @@
|
|||
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDPushConstants
|
||||
{
|
||||
protected List<float> _floats = new List<float>();
|
||||
protected int _floatIndex = 0;
|
||||
|
||||
protected List<int> _ints = new List<int>();
|
||||
protected int _intIndex = 0;
|
||||
|
||||
protected byte[] _bytes;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_floatIndex = 0;
|
||||
_intIndex = 0;
|
||||
}
|
||||
|
||||
public void Set( params object[] objects )
|
||||
{
|
||||
Reset();
|
||||
|
||||
for ( int i = 0; i < objects.Length; i++ )
|
||||
{
|
||||
if ( objects[ i ] is int )
|
||||
{
|
||||
_AddInt( (int) objects[ i ] );
|
||||
}
|
||||
|
||||
else if ( objects[ i ] is float )
|
||||
{
|
||||
_AddFloat( (float) objects[ i ] );
|
||||
}
|
||||
|
||||
else if ( objects[ i ] is Vector2 )
|
||||
{
|
||||
_AddVector2( (Vector2) objects[ i ] );
|
||||
}
|
||||
|
||||
else if ( objects[ i ] is Vector2I )
|
||||
{
|
||||
_AddVector2( (Vector2I) objects[ i ] );
|
||||
}
|
||||
|
||||
else if ( objects[ i ] is Vector3 )
|
||||
{
|
||||
_AddVector3( (Vector3) objects[ i ] );
|
||||
}
|
||||
|
||||
else if ( objects[ i ] is Vector3I )
|
||||
{
|
||||
_AddVector3( (Vector3I) objects[ i ] );
|
||||
}
|
||||
|
||||
else if ( objects[ i ] is Vector4 )
|
||||
{
|
||||
_AddVector4( (Vector4) objects[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void _AddFloat( float value )
|
||||
{
|
||||
if ( _floatIndex >= _floats.Count )
|
||||
{
|
||||
_floats.Add( value );
|
||||
_floatIndex = _floats.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_floats[ _floatIndex ] = value;
|
||||
_floatIndex ++;
|
||||
}
|
||||
}
|
||||
|
||||
protected void _AddInt( int value )
|
||||
{
|
||||
if ( _intIndex >= _ints.Count )
|
||||
{
|
||||
_ints.Add( value );
|
||||
_intIndex = _floats.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ints[ _intIndex ] = value;
|
||||
_intIndex ++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add( params float[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddFloat( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
protected void _AddVector2( Vector2 value )
|
||||
{
|
||||
_AddFloat( value.X );
|
||||
_AddFloat( value.Y );
|
||||
}
|
||||
|
||||
protected void _AddVector2I( Vector2I value )
|
||||
{
|
||||
_AddInt( value.X );
|
||||
_AddInt( value.Y );
|
||||
}
|
||||
|
||||
public void Add( params Vector2[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddVector2( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
public void Add( params Vector2I[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddVector2I( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
protected void _AddVector3( Vector3 value )
|
||||
{
|
||||
_AddFloat( value.X );
|
||||
_AddFloat( value.Y );
|
||||
_AddFloat( value.Z );
|
||||
}
|
||||
|
||||
protected void _AddVector3I( Vector3I value )
|
||||
{
|
||||
_AddInt( value.X );
|
||||
_AddInt( value.Y );
|
||||
_AddInt( value.Z );
|
||||
}
|
||||
|
||||
public void Add( params Vector3[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddVector3( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
public void Add( params Vector3I[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddVector3I( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
protected void _AddVector4( Vector4 value )
|
||||
{
|
||||
_AddFloat( value.X );
|
||||
_AddFloat( value.Y );
|
||||
_AddFloat( value.Z );
|
||||
_AddFloat( value.W );
|
||||
}
|
||||
|
||||
public void Add( params Vector4[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddVector4( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void Add( params int[] values )
|
||||
{
|
||||
for ( int i = 0; i < values.Length; i++ )
|
||||
{
|
||||
_AddInt( values[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] bytes
|
||||
{
|
||||
get
|
||||
{
|
||||
var numBytes = ( _intIndex + _floatIndex ) * 4;
|
||||
|
||||
while ( numBytes % 16 != 0 )
|
||||
{
|
||||
numBytes ++;
|
||||
}
|
||||
|
||||
if ( _bytes == null || _bytes.Length != numBytes )
|
||||
{
|
||||
_bytes = new byte[ numBytes ];
|
||||
}
|
||||
|
||||
for ( int i = 0; i < _floats.Count; i++ )
|
||||
{
|
||||
var floatBytes = BitConverter.GetBytes( _floats[ i ] );
|
||||
Array.Copy( floatBytes, 0, _bytes, i * 4, 4 );
|
||||
}
|
||||
|
||||
var floatsOffset = _floats.Count * 4;
|
||||
|
||||
for ( int i = 0; i < _ints.Count; i++ )
|
||||
{
|
||||
var intBytes = BitConverter.GetBytes( _ints[ i ] );
|
||||
Array.Copy( intBytes, 0, _bytes, i * 4 + floatsOffset, 4 );
|
||||
}
|
||||
|
||||
var intsOffset = _ints.Count * 4;
|
||||
|
||||
for ( int i = intsOffset + floatsOffset; i < _bytes.Length; i++ )
|
||||
{
|
||||
_bytes[ i ] = 0;
|
||||
}
|
||||
|
||||
return _bytes;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://crf86mq4o5ybx
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDSampler: RenderingObject
|
||||
{
|
||||
public RDSampler( RokojoriCompositorEffect effect, Rid rid ):base( effect, rid )
|
||||
{}
|
||||
|
||||
public static RDSampler Create( RokojoriCompositorEffect effect, RDSamplerState samplerState )
|
||||
{
|
||||
return new RDSampler( effect, effect.rd.SamplerCreate( samplerState ) );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://cwht50f7wwxgg
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDShader: RenderingObject
|
||||
{
|
||||
public RDShader( RokojoriCompositorEffect effect, Rid rid ):base( effect, rid )
|
||||
{}
|
||||
|
||||
public static RDShader CreateFromSpirV( RokojoriCompositorEffect effect, RDShaderSpirV rDShaderSpirV )
|
||||
{
|
||||
var shaderID = effect.rd.ShaderCreateFromSpirV( rDShaderSpirV );
|
||||
return new RDShader( effect, shaderID );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://flybmkbccn5l
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDTexture: RenderingObject
|
||||
{
|
||||
public RDTexture( RokojoriCompositorEffect effect, Rid rid ):base( effect, rid ){}
|
||||
|
||||
public static RDTexture Create( RokojoriCompositorEffect effect, RDTextureFormat format, RDTextureView view )
|
||||
{
|
||||
return new RDTexture( effect, effect.rd.TextureCreate( format, view ) );
|
||||
}
|
||||
|
||||
public static RDTexture Color( RokojoriCompositorContext context )
|
||||
{
|
||||
var rid = context.sceneBuffers.GetColorLayer( (uint) context.view );
|
||||
|
||||
return new RDTexture( context.effect, rid );
|
||||
}
|
||||
|
||||
public static RDTexture Depth( RokojoriCompositorContext context )
|
||||
{
|
||||
var rid = context.sceneBuffers.GetDepthLayer( (uint) context.view );
|
||||
|
||||
return new RDTexture( context.effect, rid );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://d3r7qnm0bl73d
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RDUniformSet:RenderingObject
|
||||
{
|
||||
protected int _setIndex = -1;
|
||||
public int setIndex => _setIndex;
|
||||
|
||||
public RDUniformSet( RokojoriCompositorEffect effect, int setIndex, Rid rid ):base( effect, rid )
|
||||
{
|
||||
_setIndex =setIndex;
|
||||
}
|
||||
|
||||
public static RDUniformSet Image( RokojoriCompositorEffect effect, RDTexture texture, int setIndex )
|
||||
{
|
||||
var uniform = new RDUniform();
|
||||
uniform.UniformType = RenderingDevice.UniformType.Image;
|
||||
uniform.Binding = 0;
|
||||
uniform.AddId( texture.rid );
|
||||
|
||||
var rd = effect.rd;
|
||||
var rid = UniformSetCacheRD.GetCache( effect.context.shader.rid, (uint) setIndex, new Array<RDUniform>{ uniform } );
|
||||
// var rid = rd.UniformSetCreate( new Array<RDUniform>{ uniform }, effect.context.shader.rid, (uint) setIndex );
|
||||
|
||||
return new RDUniformSet( effect, setIndex, rid );
|
||||
}
|
||||
|
||||
public static RDUniformSet Sampler( RokojoriCompositorEffect effect, RDSampler sampler, RDTexture texture, int setIndex )
|
||||
{
|
||||
var uniform = new RDUniform();
|
||||
uniform.UniformType = RenderingDevice.UniformType.SamplerWithTexture;
|
||||
uniform.Binding = 0;
|
||||
uniform.AddId( sampler.rid );
|
||||
uniform.AddId( texture.rid ) ;
|
||||
|
||||
var rd = effect.rd;
|
||||
|
||||
var rid = UniformSetCacheRD.GetCache( effect.context.shader.rid, (uint) setIndex, new Array<RDUniform>{ uniform } );
|
||||
// var rid = rd.UniformSetCreate( new Array<RDUniform>{ uniform }, effect.context.shader.rid, (uint) setIndex );
|
||||
|
||||
return new RDUniformSet( effect, setIndex, rid );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://cb5jtvktd1epc
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RenderingObject
|
||||
{
|
||||
protected RokojoriCompositorEffect _effect;
|
||||
public RokojoriCompositorEffect effect => _effect;
|
||||
|
||||
protected Rid _rid;
|
||||
public Rid rid => _rid;
|
||||
|
||||
public bool valid => _rid.IsValid;
|
||||
|
||||
public RenderingObject( RokojoriCompositorEffect effect, Rid rid )
|
||||
{
|
||||
this._effect = effect;
|
||||
this._rid = rid;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://xeqc03e3llpu
|
|
@ -0,0 +1,167 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RokojoriCompositorContext
|
||||
{
|
||||
protected RokojoriCompositorEffect _effect;
|
||||
public RokojoriCompositorEffect effect => _effect;
|
||||
|
||||
protected RDShader _shader;
|
||||
public RDShader shader => _shader;
|
||||
|
||||
protected RDPipeline _pipeline;
|
||||
public RDPipeline pipeline => _pipeline;
|
||||
|
||||
public void SetShaderAndPipeline( RDShader shader, RDPipeline pipeline )
|
||||
{
|
||||
effect.Verbose( "Set Shader Pipeline", shader, pipeline );
|
||||
_shader = shader;
|
||||
_pipeline = pipeline;
|
||||
|
||||
}
|
||||
|
||||
protected int _view = -1;
|
||||
public int view => _view;
|
||||
|
||||
protected int _effectCallbackType;
|
||||
public int effectCallbackType => _effectCallbackType;
|
||||
|
||||
protected RenderData _renderData;
|
||||
public RenderData renderData => _renderData;
|
||||
|
||||
|
||||
protected RenderSceneBuffersRD _sceneBuffers;
|
||||
public RenderSceneBuffersRD sceneBuffers => _sceneBuffers;
|
||||
|
||||
protected RenderSceneDataRD _sceneData;
|
||||
public RenderSceneDataRD sceneData => _sceneData;
|
||||
|
||||
protected Vector3I _groups;
|
||||
public Vector3I groups => _groups;
|
||||
|
||||
public Vector2I internalSize => _sceneBuffers.GetInternalSize();
|
||||
|
||||
public RDTexture GetScreenColorTexture()
|
||||
{
|
||||
return RDTexture.Color( this );
|
||||
}
|
||||
|
||||
public RDTexture GetScreenDepthTexture()
|
||||
{
|
||||
return RDTexture.Depth( this );
|
||||
}
|
||||
|
||||
public void Assign_ScreenColorTexture( RDSampler sampler = null, int setIndex = -1 )
|
||||
{
|
||||
AssignUniformSet_Texture( GetScreenColorTexture(), sampler, setIndex );
|
||||
}
|
||||
|
||||
public void Assign_ScreenDepthTexture( RDSampler sampler = null, int setIndex = -1 )
|
||||
{
|
||||
AssignUniformSet_Texture( GetScreenDepthTexture(), sampler, setIndex );
|
||||
}
|
||||
|
||||
public void AssignUniformSet_Texture( RDTexture texture, RDSampler sampler = null, int setIndex = -1 )
|
||||
{
|
||||
// effect.Verbose( "Incoming Uniform Index", setIndex );
|
||||
|
||||
if ( setIndex == -1 )
|
||||
{
|
||||
setIndex = _uniformSets.Count;
|
||||
}
|
||||
|
||||
// effect.Verbose( "Set Uniform Index", setIndex );
|
||||
|
||||
if ( sampler == null )
|
||||
{
|
||||
// effect.Verbose( "Adding Image" );
|
||||
AddUniformSet( RDUniformSet.Image( _effect, texture, setIndex ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// effect.Verbose( "Adding Sampler" );
|
||||
AddUniformSet( RDUniformSet.Sampler( _effect, sampler,texture, setIndex ) );
|
||||
}
|
||||
}
|
||||
|
||||
List<RDUniformSet> _uniformSets = new List<RDUniformSet>();
|
||||
public RDPushConstants pushConstants;
|
||||
|
||||
public void AddUniformSet( RDUniformSet uniformSet )
|
||||
{
|
||||
_uniformSets.Add( uniformSet );
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_uniformSets.Clear();
|
||||
pushConstants = null;
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var computeList = RDComputeList.Begin( _effect.rd );
|
||||
computeList.BindPipeline( pipeline );
|
||||
|
||||
|
||||
_uniformSets.ForEach(
|
||||
( u )=>
|
||||
{
|
||||
computeList.BindUniformSet( u, u.setIndex );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if ( pushConstants != null )
|
||||
{
|
||||
computeList.SetPushConstants( pushConstants );
|
||||
}
|
||||
|
||||
|
||||
|
||||
computeList.Dispatch( groups );
|
||||
|
||||
|
||||
computeList.End();
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
effect.Error( e );
|
||||
}
|
||||
|
||||
Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EditableRokojoriCompositorContext:RokojoriCompositorContext
|
||||
{
|
||||
public void SetEffect( RokojoriCompositorEffect effect )
|
||||
{
|
||||
this._effect = effect;
|
||||
}
|
||||
|
||||
public void SetRenderData( RenderData renderData, RenderSceneBuffersRD buffers, RenderSceneDataRD sceneData )
|
||||
{
|
||||
this._renderData = renderData;
|
||||
this._sceneBuffers = buffers;
|
||||
this._sceneData = sceneData;
|
||||
}
|
||||
|
||||
public void SetView( int view )
|
||||
{
|
||||
this._view = view;
|
||||
}
|
||||
|
||||
public void SetGroups( Vector3I groups )
|
||||
{
|
||||
this._groups = groups;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://ccu40hi7l5hmu
|
|
@ -0,0 +1,291 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class RokojoriCompositorEffect:CompositorEffect
|
||||
{
|
||||
protected RenderingDevice _rd;
|
||||
public RenderingDevice rd => _rd;
|
||||
|
||||
protected int _groupSize = 16;
|
||||
protected List<RenderingObject> _cleanUps = new List<RenderingObject>();
|
||||
protected List<string> _cleanUpInfo = new List<string>();
|
||||
|
||||
EditableRokojoriCompositorContext _context;
|
||||
protected bool _hasContext = false;
|
||||
public RokojoriCompositorContext context => _hasContext ? _context : null;
|
||||
|
||||
protected List<Message> _messages = new List<Message>();
|
||||
public List<Message> messages => _messages;
|
||||
public bool logMessages = true;
|
||||
public int messageLogLevel = Messages.GetLevel( MessageType.Verbose );
|
||||
|
||||
protected virtual void OnConfigure(){}
|
||||
protected virtual void OnInitialize(){}
|
||||
protected virtual void ForAllViews(){}
|
||||
protected virtual void RenderView(){}
|
||||
|
||||
public RokojoriCompositorEffect():base()
|
||||
{
|
||||
RenderingServer.CallOnRenderThread( Callable.From( _InitializeCompositorEffect ) );
|
||||
}
|
||||
|
||||
protected void _InitializeCompositorEffect()
|
||||
{
|
||||
OnConfigure();
|
||||
|
||||
_rd = RenderingServer.Singleton.GetRenderingDevice();
|
||||
|
||||
if ( _rd == null )
|
||||
{
|
||||
Error( "Found no rendering device" );
|
||||
return;
|
||||
}
|
||||
|
||||
_context = new EditableRokojoriCompositorContext();
|
||||
_context.SetEffect( this );
|
||||
|
||||
_hasContext = true;
|
||||
OnInitialize();
|
||||
_hasContext = false;
|
||||
|
||||
}
|
||||
|
||||
public override void _RenderCallback( int effectCallbackType, RenderData renderData )
|
||||
{
|
||||
|
||||
_hasContext = false;
|
||||
|
||||
if ( rd == null )
|
||||
{
|
||||
Error( "No render device" );
|
||||
return;
|
||||
}
|
||||
|
||||
var sceneBuffers = ( RenderSceneBuffersRD ) renderData.GetRenderSceneBuffers();
|
||||
var sceneData = ( RenderSceneDataRD ) renderData.GetRenderSceneData();
|
||||
|
||||
if ( sceneBuffers == null && sceneData == null )
|
||||
{
|
||||
Error( "sceneBuffers == null && sceneData == null" );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var size = sceneBuffers.GetInternalSize();
|
||||
|
||||
if ( size.X == 0 || size.Y == 0 )
|
||||
{
|
||||
Warning( "InternalSize.X == 0 || InternalSize.Y == 0" );
|
||||
return;
|
||||
}
|
||||
|
||||
_context.SetRenderData( renderData, sceneBuffers, sceneData );
|
||||
_context.SetView( -1 );
|
||||
_context.SetGroups( new Vector3I( 1, 1, 1 ) );
|
||||
|
||||
|
||||
|
||||
_hasContext = true;
|
||||
var groups = ComputeGroups();
|
||||
_context.SetGroups( groups );
|
||||
|
||||
ForAllViews();
|
||||
|
||||
|
||||
|
||||
int viewCount = ( int ) sceneBuffers.GetViewCount();
|
||||
|
||||
for ( int i = 0; i < viewCount; i++ )
|
||||
{
|
||||
_context.SetView( i );
|
||||
RenderView();
|
||||
}
|
||||
|
||||
_hasContext = false;
|
||||
}
|
||||
|
||||
protected virtual Vector3I ComputeGroups()
|
||||
{
|
||||
var size = context.sceneBuffers.GetInternalSize();
|
||||
|
||||
var xGroups = Mathf.CeilToInt( size.X / (float) _groupSize );
|
||||
var yGroups = Mathf.CeilToInt( size.Y / (float) _groupSize );
|
||||
|
||||
return new Vector3I( xGroups, yGroups, 1 );
|
||||
}
|
||||
|
||||
|
||||
public override void _Notification( int what )
|
||||
{
|
||||
var _shader = context.shader;
|
||||
|
||||
Verbose( "Got notification: ", what );
|
||||
|
||||
if ( what != NotificationPredelete || ( _shader == null || !_shader.valid ) || rd == null )
|
||||
{
|
||||
Verbose(
|
||||
"what != NotificationPredelete", what != NotificationPredelete,
|
||||
"( _shader == null || !_shader.valid )", ( _shader == null || !_shader.valid ),
|
||||
"rd == null", rd == null
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
CleanUp( _shader, "Shader" );
|
||||
|
||||
var index = 0;
|
||||
|
||||
_cleanUps.ForEach(
|
||||
c =>
|
||||
{
|
||||
CleanUp( c, "_cleanUps[" + index + "]");
|
||||
index ++;
|
||||
}
|
||||
);
|
||||
|
||||
_cleanUps.Clear();
|
||||
|
||||
}
|
||||
|
||||
public void AddToCleanUp( RenderingObject ro, string info = null )
|
||||
{
|
||||
var _shader = context.shader;
|
||||
|
||||
if ( _cleanUps.Contains( ro ) || _shader == ro )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cleanUps.Add( ro );
|
||||
_cleanUpInfo.Add( info );
|
||||
}
|
||||
|
||||
public void AddToCleanUp( List<RenderingObject> ro, string info = null )
|
||||
{
|
||||
var index = 0;
|
||||
info = info == null ? "" : info;
|
||||
|
||||
ro.ForEach(
|
||||
r =>
|
||||
{
|
||||
AddToCleanUp( r, info + "["+ index + "]" );
|
||||
index ++;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void CleanUp( RenderingObject ro, string info )
|
||||
{
|
||||
if ( ro == null )
|
||||
{
|
||||
Warning( "ro == null, couldn't clean up: ", info );
|
||||
return;
|
||||
}
|
||||
|
||||
Verbose( "Cleaning up: ", info, ro.rid );
|
||||
rd.FreeRid( ro.rid );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
// ---------------------------------------------
|
||||
// CONVINIENCE
|
||||
// ---------------------------------------------
|
||||
// ---------------------------------------------
|
||||
|
||||
public RDSampler Sampler( RDSamplerState state = null)
|
||||
{
|
||||
if ( state == null )
|
||||
{
|
||||
state = new RDSamplerState();
|
||||
state.MinFilter = RenderingDevice.SamplerFilter.Linear;
|
||||
state.MagFilter = RenderingDevice.SamplerFilter.Linear;
|
||||
state.RepeatU = RenderingDevice.SamplerRepeatMode.Repeat;
|
||||
state.RepeatV = RenderingDevice.SamplerRepeatMode.Repeat;
|
||||
}
|
||||
|
||||
var sampler = RDSampler.Create( this, state );
|
||||
return sampler;
|
||||
}
|
||||
|
||||
public RDSampler Sampler( RenderingDevice.SamplerFilter filter, RenderingDevice.SamplerRepeatMode repeatMode)
|
||||
{
|
||||
var state = new RDSamplerState();
|
||||
|
||||
state.MinFilter = filter;
|
||||
state.MagFilter = filter;
|
||||
|
||||
state.RepeatU = repeatMode;
|
||||
state.RepeatV = repeatMode;
|
||||
|
||||
return Sampler( state );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
// ---------------------------------------------
|
||||
// MESSAGES
|
||||
// ---------------------------------------------
|
||||
// ---------------------------------------------
|
||||
|
||||
|
||||
public void Error( params object[] messages )
|
||||
{
|
||||
var message = RJLog.GetLogString( messages );
|
||||
|
||||
Messages.Error( _messages, message );
|
||||
|
||||
if ( logMessages )
|
||||
{
|
||||
this.LogError( message );
|
||||
}
|
||||
}
|
||||
|
||||
public void Warning( params object[] messages )
|
||||
{
|
||||
var message = RJLog.GetLogString( messages );
|
||||
|
||||
Messages.Warning( _messages, message );
|
||||
|
||||
if ( logMessages && Messages.GetLevel( MessageType.Warning ) >= messageLogLevel )
|
||||
{
|
||||
this.LogInfo( message );
|
||||
}
|
||||
}
|
||||
|
||||
public void Info( params object[] messages )
|
||||
{
|
||||
var message = RJLog.GetLogString( messages );
|
||||
|
||||
Messages.Info( _messages, message );
|
||||
|
||||
if ( logMessages && Messages.GetLevel( MessageType.Info ) >= messageLogLevel )
|
||||
{
|
||||
this.LogInfo( message );
|
||||
}
|
||||
}
|
||||
|
||||
public void Verbose( params object[] messages )
|
||||
{
|
||||
var message = RJLog.GetLogString( messages );
|
||||
|
||||
Messages.Verbose( _messages, message );
|
||||
|
||||
if ( logMessages && Messages.GetLevel( MessageType.Verbose ) >= messageLogLevel )
|
||||
{
|
||||
this.LogInfo( message );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://chfoyvoiddqc3
|
|
@ -0,0 +1 @@
|
|||
uid://5hwmnvb3xo7j
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[Tool]
|
||||
[GlobalClass]
|
||||
public partial class SingleShaderCompositorEffect:RokojoriCompositorEffect
|
||||
{
|
||||
protected string _shaderPath;
|
||||
|
||||
protected RDShader _shader;
|
||||
protected RDPipeline _pipeline;
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Verbose( "Trying to load shader: ", _shaderPath );
|
||||
|
||||
if ( _shaderPath == null )
|
||||
{
|
||||
Error( "_shaderPath == null" );
|
||||
return;
|
||||
}
|
||||
|
||||
var glslFile = GD.Load<RDShaderFile>( _shaderPath );
|
||||
|
||||
if ( glslFile == null )
|
||||
{
|
||||
Error( "Couldn't load shader at path:", _shaderPath );
|
||||
return;
|
||||
}
|
||||
|
||||
_shader = RDShader.CreateFromSpirV( this, glslFile.GetSpirV() );
|
||||
|
||||
if ( _shader == null )
|
||||
{
|
||||
Error( "Couldn't create shader from code, path:", _shaderPath );
|
||||
return;
|
||||
}
|
||||
|
||||
_pipeline = RDPipeline.Create( this, _shader );
|
||||
|
||||
if ( _shader == null )
|
||||
{
|
||||
Error( "Couldn't create pipeline from compiled shader, path:", _shaderPath );
|
||||
return;
|
||||
}
|
||||
|
||||
Verbose( "Created shader at path: ", _shaderPath );
|
||||
|
||||
context.SetShaderAndPipeline( _shader, _pipeline );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uid://dkcn0rdtj7arp
|
Binary file not shown.
|
@ -304,8 +304,6 @@ namespace Rokojori
|
|||
list.RemoveAll( e => removalSet.Contains( e ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static int CountItems<T>( List<T> list, Predicate<T> test )
|
||||
{
|
||||
var result = 0;
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Rokojori
|
|||
{
|
||||
public class Safe
|
||||
{
|
||||
static readonly int maxWhileIterations = 1000 * 1000;
|
||||
static readonly int maxWhileIterations = 2000 * 1000;
|
||||
|
||||
public static void While( Func<bool> condition, System.Action action, System.Action onTooManyIterations = null )
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue