rj-action-library/Runtime/Procedural/Scatter/Scatterer.cs

260 lines
5.7 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/Scatterer.svg") ]
public partial class Scatterer:Node3D
{
[Export]
public bool update = false;
[Export]
public bool updateAlways = false;
[Export]
public Node3D[] containersToClearNodes;
[Export]
public bool clearContainers = false;
[Export]
public bool removeDiscarded = false;
[Export]
public int READ_ONLY_createdPoints = 0;
[Export]
public int READ_ONLY_instantiatedPoints = 0;
[Export]
public int READ_ONLY_reusedPoints = 0;
[Export]
public int READ_ONLY_remappedPoints = 0;
public override void _Process( double delta )
{
if ( clearContainers )
{
clearContainers = false;
ClearContainers();
}
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
ScatterAndInstantiatePoints();
}
public void ClearContainers()
{
Arrays.ForEach( containersToClearNodes,
c =>
{
Nodes.RemoveAndDeleteChildren( c );
}
);
}
class InstantiatedScatterPoint
{
public string hash;
public ScatterPoint scatterPoint;
public Node3D output;
}
Dictionary<string,InstantiatedScatterPoint> _instantiatedPoints = new Dictionary<string, InstantiatedScatterPoint>();
Dictionary<Scatterer,int> _scattererIDs = new Dictionary<Scatterer, int>();
int GetScattererID( Scatterer s )
{
if ( ! _scattererIDs.ContainsKey( s ) )
{
_scattererIDs[ s ] = _scattererIDs.Count;
}
return _scattererIDs[ s ];
}
string GetScatterPointHash( ScatterPoint p )
{
var scatterID = GetScattererID( p.creator ) + ":" + p.creatorID;
return scatterID;
}
public void ScatterAndInstantiatePoints()
{
if ( _instantiatedPoints.Count == 0 )
{
ClearContainers();
}
var points = Scatter( new List<ScatterPoint>() );
READ_ONLY_createdPoints = points.Count;
READ_ONLY_instantiatedPoints = 0;
READ_ONLY_reusedPoints = 0;
READ_ONLY_remappedPoints = 0;
var usedPoints = new HashSet<string>();
points.RemoveAll( p => ! p.visible );
points.ForEach( p =>
{
var hash = GetScatterPointHash( p );
if ( ! CanReuse( hash, p ) )
{
return;
}
var existing = _instantiatedPoints[ hash ];
existing.scatterPoint = p;
existing.scatterPoint.UpdateInstantiated( existing.output );
usedPoints.Add( hash );
READ_ONLY_reusedPoints ++;
return;
}
);
var unused = new DictionaryList<PackedScene,InstantiatedScatterPoint>();
foreach ( var vk in _instantiatedPoints )
{
var ip = vk.Value;
if ( usedPoints.Contains( ip.hash ) )
{
continue;
}
unused.Add( ip.scatterPoint.scene, ip );
}
points.ForEach( p =>
{
var hash = GetScatterPointHash( p );
if ( usedPoints.Contains( hash ) )
{
return;
}
if ( unused.ContainsKey( p.scene ) )
{
var unusedPoint = unused[ p.scene ].Find( ip => ip.scatterPoint.CanBeReusedBy( p ) );
if ( unusedPoint != null )
{
unused.Remove( p.scene, unusedPoint );
unusedPoint.scatterPoint = p;
unusedPoint.scatterPoint.UpdateInstantiated( unusedPoint.output );
usedPoints.Add( hash );
READ_ONLY_remappedPoints ++;
return;
}
}
var instantiatedOutput = p.Instantiate();
if ( instantiatedOutput == null )
{
return;
}
var ip = new InstantiatedScatterPoint();
ip.hash = hash;
ip.output = instantiatedOutput;
ip.scatterPoint = p;
if ( _instantiatedPoints.ContainsKey( hash ) )
{
Nodes.RemoveAndDelete( _instantiatedPoints[ hash ].output );
}
_instantiatedPoints[ hash ] = ip;
usedPoints.Add( hash );
READ_ONLY_instantiatedPoints++;
}
);
Dictionaries.RemoveAll(
_instantiatedPoints, ( k, v ) =>
{
if ( usedPoints.Contains( k ) )
{
return false;
}
Nodes.RemoveAndDelete( v.output );
return true;
}
);
}
bool CanReuse( string hash, ScatterPoint sp )
{
if ( ! _instantiatedPoints.ContainsKey( hash ) )
{
return false;
}
var existing = _instantiatedPoints[ hash ];
var canBeReused = existing.scatterPoint.CanBeReusedBy( sp );
return canBeReused;
}
public List<ScatterPoint> Scatter( List<ScatterPoint> points )
{
var returnedPoints = _Scatter( points );
if ( removeDiscarded )
{
returnedPoints.RemoveAll( p => ! p.visible );
}
return returnedPoints;
}
protected bool IsChildEnabled( Scatterer s, bool childrenNeedToBeVisible, bool childrenNeedToBeProcessing )
{
var enabled = true;
if ( childrenNeedToBeVisible )
{
enabled = enabled && s.Visible;
}
if ( childrenNeedToBeProcessing )
{
enabled = enabled && ( s.ProcessMode != ProcessModeEnum.Disabled );
}
return enabled;
}
protected virtual List<ScatterPoint> _Scatter( List<ScatterPoint> points )
{
return points;
}
}
}