using System.Text.Json;
using System.Text.Json.Nodes;
namespace Sandbox;
public sealed partial class SkinnedModelRenderer
{
///
/// If something sets parameters before the model is spawned, then we store them
/// and apply them when it does spawn. This isn't ideal, but it is what it is.
///
readonly Dictionary parameters = new( StringComparer.OrdinalIgnoreCase );
public void Set( string v, Vector3 value )
{
parameters[v] = value;
SceneModel?.SetAnimParameter( v, value );
}
public void Set( string v, int value )
{
parameters[v] = value;
SceneModel?.SetAnimParameter( v, value );
}
public void Set( string v, float value )
{
parameters[v] = value;
SceneModel?.SetAnimParameter( v, value );
}
public void Set( string v, bool value )
{
parameters[v] = value;
SceneModel?.SetAnimParameter( v, value );
}
public void Set( string v, Rotation value )
{
parameters[v] = value;
SceneModel?.SetAnimParameter( v, value );
}
void ApplyStoredAnimParameters()
{
foreach ( var p in parameters )
{
if ( p.Value is Vector3 v ) SceneModel.SetAnimParameter( p.Key, v );
if ( p.Value is float f ) SceneModel.SetAnimParameter( p.Key, f );
if ( p.Value is int i ) SceneModel.SetAnimParameter( p.Key, i );
if ( p.Value is bool b ) SceneModel.SetAnimParameter( p.Key, b );
if ( p.Value is Rotation r ) SceneModel.SetAnimParameter( p.Key, r );
}
}
///
/// Remove any stored parameters
///
public void ClearParameters()
{
parameters.Clear();
if ( SceneModel.IsValid() )
{
SceneModel.ResetAnimParameters();
}
}
internal void ClearParameter( string name )
{
parameters.Remove( name );
}
internal bool ContainsParameter( string name )
{
return parameters.ContainsKey( name );
}
// public void Set( string v, Enum value ) => _sceneObject.SetAnimParameter( v, value );
public bool GetBool( string v ) => SceneModel?.GetBool( v ) ?? false;
public int GetInt( string v ) => SceneModel?.GetInt( v ) ?? 0;
public float GetFloat( string v ) => SceneModel?.GetFloat( v ) ?? 0.0f;
public Vector3 GetVector( string v ) => SceneModel?.GetVector3( v ) ?? Vector3.Zero;
public Rotation GetRotation( string v ) => SceneModel?.GetRotation( v ) ?? Rotation.Identity;
///
/// Converts value to vector local to this entity's eyepos and passes it to SetAnimVector
///
public void SetLookDirection( string name, Vector3 eyeDirectionWorld )
{
var delta = eyeDirectionWorld * WorldRotation.Inverse;
Set( name, delta );
}
///
/// Converts value to vector local to this entity's eyepos and passes it to SetAnimVector.
/// This also sets {name}_weight to the weight value.
///
public void SetLookDirection( string name, Vector3 eyeDirectionWorld, float weight )
{
var delta = eyeDirectionWorld * WorldRotation.Inverse;
Set( name, delta );
Set( $"{name}_weight", weight );
}
///
/// Sets an IK parameter. This sets 3 variables that should be set in the animgraph:
/// 1. ik.{name}.enabled
/// 2. ik.{name}.position
/// 3. ik.{name}.rotation
///
public void SetIk( string name, Transform tx )
{
// convert local to model
tx = WorldTransform.ToLocal( tx );
Set( $"ik.{name}.enabled", true );
Set( $"ik.{name}.position", tx.Position );
Set( $"ik.{name}.rotation", tx.Rotation );
}
///
/// This sets ik.{name}.enabled to false.
///
public void ClearIk( string name )
{
Set( $"ik.{name}.enabled", false );
}
ParameterAccessor _parameters;
///
/// Access to the animgraph parameters for this model
///
[Property, Group( "Parameters", StartFolded = true ), ShowIf( nameof( ShouldShowParametersEditor ), true )]
public ParameterAccessor Parameters
{
get
{
_parameters ??= new( this );
return _parameters;
}
}
public bool ShouldShowParametersEditor
{
get
{
if ( !UseAnimGraph ) return false;
if ( !SceneModel.IsValid() ) return false;
var graph = SceneModel.AnimationGraph;
if ( graph is null ) return false;
if ( graph.ParamCount <= 0 ) return false;
return true;
}
}
public sealed class ParameterAccessor : IJsonPopulator
{
public AnimationGraph Graph => _renderer.IsValid() && _renderer.SceneModel.IsValid() ?
_renderer.SceneModel.AnimationGraph : null;
readonly SkinnedModelRenderer _renderer;
readonly Dictionary _bools = new( StringComparer.OrdinalIgnoreCase );
readonly Dictionary _ints = new( StringComparer.OrdinalIgnoreCase );
readonly Dictionary _floats = new( StringComparer.OrdinalIgnoreCase );
readonly Dictionary _vectors = new( StringComparer.OrdinalIgnoreCase );
readonly Dictionary _rotations = new( StringComparer.OrdinalIgnoreCase );
internal ParameterAccessor( SkinnedModelRenderer renderer )
{
_renderer = renderer;
}
public void Clear()
{
_bools.Clear();
_ints.Clear();
_floats.Clear();
_vectors.Clear();
_rotations.Clear();
_renderer.ClearParameters();
}
public void Reset( string name )
{
var parameter = Graph.GetParameterFromList( name );
if ( parameter.IsNull )
return;
var defaultValue = parameter.GetDefaultValue();
switch ( parameter.GetParameterType() )
{
case NativeEngine.AnimParamType.Float:
Set( name, defaultValue.GetValue() );
break;
case NativeEngine.AnimParamType.Int:
Set( name, defaultValue.GetValue() );
break;
case NativeEngine.AnimParamType.Enum:
Set( name, defaultValue.GetValue() );
break;
case NativeEngine.AnimParamType.Bool:
Set( name, defaultValue.GetValue() );
break;
case NativeEngine.AnimParamType.Vector:
Set( name, defaultValue.GetValue() );
break;
case NativeEngine.AnimParamType.Rotation:
Set( name, defaultValue.GetValue() );
break;
default:
throw new NotSupportedException( $"Unsupported parameter type: {parameter.GetParameterType()}" );
}
}
public void Clear( string name )
{
Reset( name );
_bools.Remove( name );
_ints.Remove( name );
_floats.Remove( name );
_vectors.Remove( name );
_rotations.Remove( name );
_renderer.ClearParameter( name );
}
public bool Contains( string name )
{
return _renderer.ContainsParameter( name );
}
public bool GetBool( string v ) => _renderer.GetBool( v );
public int GetInt( string v ) => _renderer.GetInt( v );
public float GetFloat( string v ) => _renderer.GetFloat( v );
public Vector3 GetVector( string v ) => _renderer.GetVector( v );
public Rotation GetRotation( string v ) => _renderer.GetRotation( v );
public void Set( string v, Vector3 value )
{
_vectors[v] = value;
_renderer.Set( v, value );
}
public void Set( string v, int value )
{
_ints[v] = value;
_renderer.Set( v, value );
}
public void Set( string v, float value )
{
_floats[v] = value;
_renderer.Set( v, value );
}
public void Set( string v, bool value )
{
_bools[v] = value;
_renderer.Set( v, value );
}
public void Set( string v, Rotation value )
{
_rotations[v] = value;
_renderer.Set( v, value );
}
JsonNode IJsonPopulator.Serialize()
{
var obj = new JsonObject();
var boolsObj = new JsonObject();
var intsObj = new JsonObject();
var floatsObj = new JsonObject();
var vectorsObj = new JsonObject();
var rotationsObj = new JsonObject();
foreach ( var value in _bools )
{
boolsObj.Add( value.Key, value.Value );
}
foreach ( var value in _ints )
{
intsObj.Add( value.Key, value.Value );
}
foreach ( var value in _floats )
{
floatsObj.Add( value.Key, value.Value );
}
foreach ( var value in _vectors )
{
vectorsObj.Add( value.Key, JsonSerializer.SerializeToNode( value.Value ) );
}
foreach ( var value in _rotations )
{
rotationsObj.Add( value.Key, JsonSerializer.SerializeToNode( value.Value ) );
}
obj.Add( "bools", boolsObj );
obj.Add( "ints", intsObj );
obj.Add( "floats", floatsObj );
obj.Add( "vectors", vectorsObj );
obj.Add( "rotations", rotationsObj );
return obj;
}
void IJsonPopulator.Deserialize( JsonNode e )
{
if ( e is not JsonObject jso )
return;
_bools.Clear();
_ints.Clear();
_floats.Clear();
_vectors.Clear();
_rotations.Clear();
if ( jso.TryGetPropertyValue( "bools", out var boolsNode ) && boolsNode is JsonObject boolsObj )
{
foreach ( var o in boolsObj )
{
Set( o.Key, o.Value.GetValue() );
}
}
if ( jso.TryGetPropertyValue( "ints", out var intsNode ) && intsNode is JsonObject intsObj )
{
foreach ( var o in intsObj )
{
Set( o.Key, o.Value.GetValue() );
}
}
if ( jso.TryGetPropertyValue( "floats", out var floatsNode ) && floatsNode is JsonObject floatsObj )
{
foreach ( var o in floatsObj )
{
Set( o.Key, o.Value.GetValue() );
}
}
if ( jso.TryGetPropertyValue( "vectors", out var vectorsNode ) && vectorsNode is JsonObject vectorsObj )
{
foreach ( var o in vectorsObj )
{
Set( o.Key, JsonSerializer.Deserialize( o.Value ) );
}
}
if ( jso.TryGetPropertyValue( "rotations", out var rotationsNode ) && rotationsNode is JsonObject rotationsObj )
{
foreach ( var o in rotationsObj )
{
Set( o.Key, JsonSerializer.Deserialize( o.Value ) );
}
}
}
}
}