Player/Camera Updates

This commit is contained in:
Josef 2025-05-21 22:44:28 +02:00
parent 3b74292b2c
commit af3786e6b4
65 changed files with 2619 additions and 431 deletions

View File

@ -99,6 +99,7 @@ namespace Rokojori
public void Highlight( HighlightActionType type, Node3D[] targets )
{
if ( HighlightActionType.Start == type )
{
StartHighlight( targets );
@ -131,6 +132,7 @@ namespace Rokojori
var outlineMaterial = new OutlineMaterial();
outlineMaterial.albedo.Set( colorTransparent );
material = outlineMaterial;
outlineMaterial.size.Set( 2 );
outlineMaterial.opacityModulationDuration.Set( opacityModulationDuration );
outlineMaterial.opacityModulationStrength.Set( opacityModulationStrength );

View File

@ -31,5 +31,5 @@ opacityModulationStrength = 0.75
opacityModulationDuration = 1.0
opacityModulationTransition = 1.0
outlineMaterialMode = 0
overlayOpacity = 0.01
overlayOpacity = 0.1
overlayMaterialMode = 0

View File

@ -24,6 +24,16 @@ namespace Rokojori
}
public static float ComputeCoefficientForFramesAdaptiveTreshold( int numFrames )
{
var fps60 = 1f/60f;
var treshold = MathX.MapClamped( numFrames, 400, 600, fps60/50, fps60/2 );
return ComputeCoefficientForFrames( numFrames, treshold );
}
public static float ComputeCoefficientForFrames( int numFrames, float treshold = 1f/60f * 0.02f )
{

View File

@ -8,12 +8,12 @@ namespace Rokojori
[GlobalClass]
public partial class FrameSmoothing: Smoothing
{
[Export( PropertyHint.Range, "0,600")]
[Export( PropertyHint.Range, "0,1800")]
public float frames = 10;
protected override float _ComputeInterpolationAmount( float delta )
{
frames = Mathf.Clamp( frames, 0, 600 );
frames = Mathf.Clamp( frames, 0, 1800 );
if ( frames <= 0 )
{
@ -36,7 +36,7 @@ namespace Rokojori
public static float ComputeCoefficientInt( float delta, int frames )
{
frames = Mathf.Clamp( frames, 0, 600 );
frames = Mathf.Clamp( frames, 0, 1800 );
if ( frames <= 0 )
{

File diff suppressed because it is too large Load Diff

View File

@ -11,19 +11,12 @@ namespace Rokojori
[Export]
public string path;
[Export]
public bool compute
{
get { return false; }
set
{
if ( ! value )
{
return;
}
[ExportToolButton( "Compute")]
public Callable ComputeButton => Callable.From( Compute );
FrameSmoothingTable.ComputeAndSaveTable( path );
}
async void Compute()
{
await FrameSmoothingTable.ComputeAndSaveTable( this, path );
}
}

View File

@ -9,12 +9,44 @@ namespace Rokojori
public partial class Smoothing: Resource
{
float _currentFloat = 0;
public void SetCurrent( float value )
{
_currentFloat = value;
}
Vector2 _currentVector2 = Vector2.Zero;
Vector3 _currentVector3 = Vector3.Zero;
Vector4 _currentVector4 = Vector4.Zero;
public void SetCurrent( Vector2 value )
{
_currentVector2 = value;
}
public void SetCurrent( Vector3 value )
{
_currentVector3 = value;
}
public void SetCurrent( Vector4 value )
{
_currentVector4 = value;
}
Quaternion _currentQuaternion = Quaternion.Identity;
public void SetCurrent( Quaternion quaternion )
{
_currentQuaternion = quaternion;
}
Color _currentColor = Colors.Black;
public void SetCurrent( Color color )
{
_currentColor = color;
}
public float Smooth( float nextValue, float delta )
{
@ -29,7 +61,16 @@ namespace Rokojori
return sm.Smooth( value, delta );
}
public Vector2 Smooth( Vector2 nextValue, float delta )
public static float ApplyWith( Smoothing sm, float oldValue, float nextValue, float delta )
{
if ( sm == null ){ return nextValue; }
sm._currentFloat = oldValue;
return sm.Smooth( nextValue, delta );
}
public Vector2 Smooth( Vector2 nextValue, float delta )
{
_currentVector2 = _currentVector2.Lerp( nextValue, _ComputeInterpolationAmount( delta ) );
@ -42,6 +83,30 @@ namespace Rokojori
return sm.Smooth( value, delta );
}
public static float ApplyDegrees( Smoothing sm, float value, float delta )
{
var newV = MathX.RadiansToVector2( MathX.DegreesToRadians * value );
return MathX.RadiansToDegrees * MathX.Vector2ToRadians( Apply( sm, newV, delta ) );
}
public static float ApplyDegreesWith( Smoothing sm, float oldValue, float nextValue, float delta )
{
var oldV = MathX.RadiansToVector2( MathX.DegreesToRadians * oldValue );
var newV = MathX.RadiansToVector2( MathX.DegreesToRadians * nextValue );
return MathX.RadiansToDegrees * MathX.Vector2ToRadians( ApplyWith( sm, oldV, newV, delta ) );
}
public static Vector2 ApplyWith( Smoothing sm, Vector2 oldValue, Vector2 nextValue, float delta )
{
if ( sm == null ){ return nextValue; }
sm._currentVector2 = oldValue;
return sm.Smooth( nextValue, delta );
}
public Vector3 Smooth( Vector3 nextValue, float delta )
{
_currentVector3 = _currentVector3.Lerp( nextValue, _ComputeInterpolationAmount( delta ) );
@ -70,7 +135,8 @@ namespace Rokojori
public Quaternion Smooth( Quaternion nextValue, float delta )
{
_currentQuaternion = _currentQuaternion.Slerp( nextValue, _ComputeInterpolationAmount( delta ) );
_currentQuaternion = _currentQuaternion.GetNormalized();
_currentQuaternion = _currentQuaternion.Slerp( nextValue.GetNormalized(), _ComputeInterpolationAmount( delta ) );
return _currentQuaternion;
}
@ -109,16 +175,18 @@ namespace Rokojori
var duration = 0f;
var lastValue = value;
var maxDuration = 30;
var maxDuration = 120;
while ( value > tresholdValue && duration < maxDuration )
{
lastValue = value;
value = Smooth( 0, delta );
Safe.While(
()=> value > tresholdValue && duration < maxDuration,
()=>
{
lastValue = value;
value = Smooth( 0, delta );
duration += delta;
}
duration += delta;
}
);
_currentFloat = cached;

View File

@ -76,8 +76,22 @@ namespace Rokojori
);
}
void InitializeCamera()
{
var cameraManager = this.Get<VirtualCamera3DManager>();
var camera = this.Get<Camera3D>();
cameraManager.camera = camera;
var slot = cameraManager.CreateChild<VirtualCamera3DSlot>();
slot.camera = this.Get<MouseEditorCamera>();
}
}
}

View File

@ -13,6 +13,11 @@ namespace Rokojori
return Directory.Exists( path );
}
public static bool FileExists( string path )
{
return File.Exists( path );
}
public static void CreateDirectory( string path )
{
Directory.CreateDirectory( path );

View File

@ -9,6 +9,16 @@ namespace Rokojori
{
public static class Nodes
{
public static T Get<T>( this Node node ) where T:Node
{
return GetAnyChild<T>( node );
}
public static T GetChild<T>( this Node node ) where T:Node
{
return GetDirectChild<T>( node );
}
public static void CopyData<T>( T from, T to ) where T:Node
{
var memberInfos = ReflectionHelper.GetDataMemberInfos<T>( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );

View File

@ -5,7 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Caster:Node3D
{

View File

@ -5,6 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class CharacterController:Node
{
@ -28,14 +29,10 @@ namespace Rokojori
[Export]
public float rotationSmoothingDuration = 0;
Smoother rotationSmoother = new Smoother();
public Smoothing rotationSmoothing;
[Export]
public float positionSmoothingDuration = 0;
Smoother positionSmoother = new Smoother();
public Smoothing positionSmoothing;
public float delta = 0;
@ -49,22 +46,24 @@ namespace Rokojori
if ( CharacterUpdateMode.Process == characterUpdateMode )
{
this.delta = (float)delta;
Nodes.ForEachDirectChild<CharacterControllerAction>( actionsContainer, a => Action.Trigger( a ) );
this.delta = (float) delta;
Nodes.ForEachDirectChild<CharacterControllerAction>( actionsContainer, Action.Trigger );
}
// positionSmoother.CopyPosition( graphics, body, rotationSmoothingDuration, delta );
// rotationSmoother.CopyRotation( graphics, body, positionSmoothingDuration, delta );
Pose.CopyTo( body, graphics );
graphics.GlobalPosition = Smoothing.Apply( positionSmoothing, body.GlobalPosition, this.delta );
graphics.SetGlobalQuaternion( Smoothing.Apply( rotationSmoothing, body.GetGlobalQuaternion(), this.delta ) );
// Pose.CopyTo( body, graphics );
}
public override void _PhysicsProcess( double delta )
{
if ( CharacterUpdateMode.Physics_Process == characterUpdateMode )
{
this.delta = (float)delta;
Nodes.ForEachDirectChild<CharacterControllerAction>( actionsContainer, a => Action.Trigger( a ) );
this.delta = (float) delta;
Nodes.ForEachDirectChild<CharacterControllerAction>( actionsContainer, Action.Trigger );
}
}
}

View File

@ -5,6 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class CharacterControllerAction:Action
{

View File

@ -5,30 +5,18 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class CharacterMovement:CharacterControllerAction
{
[Export]
public float onFloorMultiply = 0f;
public float onFloorMultiply = 1f;
[Export]
public bool overwriteVelocity = true;
[ExportGroup( "Actions" )]
[Export]
public Action onMoving;
[Export]
public Action onIdle;
public float inAirMultiply = 0.01f;
[Export]
public Action onForward;
[Export]
public Action onBackwards;
[Export]
public Action onStrafeLeft;
[Export]
public Action onStrafeRight;
public CameraTargetOffset cameraTargetOffset;
[ExportGroup( "Direction Source" )]
[Export]
@ -41,32 +29,26 @@ namespace Rokojori
Project_On_TransformPlane
}
[Export]
public DirectionProcessing directionProcessing;
[ExportGroup( "Moving" )]
[ExportGroup("Movement")]
[Export]
public float moveSpeed;
[Export]
public Sensor forward;
public CharacterMovementType controllerMovementType;
[Export]
public Sensor backwards;
[ExportGroup( "Strafing" )]
[Export]
public float strafeSpeed;
public CharacterMovementType mouseKeyboardMovementType;
[Export]
public Sensor strafeLeft;
public CharacterMovementType currentMovementType;
[Export]
public Sensor strafeRight;
public Smoothing onFloorMovementSmoothing = new FrameSmoothing();
[Export]
public bool useBodyDirection = true;
public Smoothing inAirMovementSmoothing = new FrameSmoothing();
[ExportGroup( "Rotation" )]
@ -74,74 +56,73 @@ namespace Rokojori
public bool adjustRotation = true;
[Export]
public Curve forwardToRotation = MathX.Curve( 0, 1 );
public Curve forwardToRotationSmoothingFrames = MathX.Curve( 0, 1 );
[Export( PropertyHint.Range, "0,1")]
public float rotationSmoothingCoefficient = 0.1f;
Smoother rotationSmoother = new Smoother();
[Export(PropertyHint.Range, "0,600")]
public int airRotationSmoothingFrames = 120;
FrameSmoothing rotationSmoothing = new FrameSmoothing();
protected CharacterMovementData characterMovementData = new CharacterMovementData();
protected override void _OnTrigger()
{
if ( ! body.IsOnFloor() )
var onFloor = body.IsOnFloor();
characterMovementData.Reset( this );
if ( currentMovementType == null )
{
return;
}
var movement = Vector3.Zero;
currentMovementType.ProcessMovement( characterMovementData );
var fw = Sensors.GetValue( forward );
var bw = Sensors.GetValue( backwards );
var fbAxis = Sensors.PolarAxis( backwards, forward );
var dir = directionSource != null ? directionSource : body;
var forwardDirection = dir.GlobalForward();
var rightDirection = useBodyDirection ? body.GlobalRight() : dir.GlobalRight();
if ( DirectionProcessing.Zero_Y_And_Normalize == directionProcessing )
if ( cameraTargetOffset != null )
{
forwardDirection.Y = 0;
rightDirection.Y = 0;
}
else if ( DirectionProcessing.Project_On_TransformPlane == directionProcessing )
{
var bodyPlane = Plane3.CreateFromNode3D( body );
forwardDirection = bodyPlane.ConstrainToPlane( forwardDirection + body.GlobalPosition ) - body.GlobalPosition;
rightDirection = bodyPlane.ConstrainToPlane( rightDirection + body.GlobalPosition ) - body.GlobalPosition;
cameraTargetOffset.SetMovingForward( characterMovementData.isMovingForward ? 1f : 0f );
}
forwardDirection = forwardDirection.Normalized();
rightDirection = rightDirection.Normalized();
var movementMultiply = onFloor ? onFloorMultiply : inAirMultiply;
movement += forwardDirection * Sensors.PolarAxis( backwards, forward ) * moveSpeed;
characterMovementData.movement *= movementMultiply;
movement += rightDirection * Sensors.PolarAxis( strafeLeft, strafeRight ) * strafeSpeed ;
if ( body.IsOnFloor() )
{
movement *= onFloorMultiply;
}
if ( adjustRotation )
{
var currentRotation = body.GetGlobalQuaternion();
var nextRotation = Math3D.LookRotation( forwardDirection );
var nextRotation = Math3D.LookRotation( characterMovementData.forwardDirection );
var speed = MathX.Clamp01( characterMovementData.movement.Length() / moveSpeed );
var sm = 1f - rotationSmoothingCoefficient;
sm = sm * sm;
if ( speed > 0 )
{
Quaternion rotation;
var speed = MathX.Clamp01( movement.Length() / moveSpeed ) * Mathf.Max( fw, bw );
if ( onFloor )
{
rotationSmoothing.frames = Mathf.Round( forwardToRotationSmoothingFrames.Sample( speed ) );
}
else
{
rotationSmoothing.frames = airRotationSmoothingFrames;
}
sm *= forwardToRotation.Sample( speed );
var rotation = rotationSmoother.SmoothWithCoefficient( currentRotation, nextRotation, sm, controller.delta );
body.SetGlobalQuaternion( rotation );
rotation = rotationSmoothing.Smooth( nextRotation, controller.delta );
body.SetGlobalQuaternion( rotation );
}
}
Velocity( movement, overwriteVelocity );
var smoothedMovement = Vector3.Zero;
if ( onFloor )
{
smoothedMovement = onFloorMovementSmoothing.Smooth( characterMovementData.movement, controller.delta );
}
else
{
smoothedMovement = inAirMovementSmoothing.Smooth( characterMovementData.movement, controller.delta );
}
Velocity( smoothedMovement, onFloor );
}
}

View File

@ -0,0 +1,14 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
public enum CharacterDirectionProcessing
{
None,
Zero_Y_And_Normalize,
Project_On_TransformPlane
}
}

View File

@ -0,0 +1,23 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
public class CharacterMovementData
{
public CharacterMovement characterMovement;
public Vector3 movement;
public Vector3 forwardDirection;
public bool isMovingForward;
public void Reset( CharacterMovement characterMovement )
{
this.characterMovement = characterMovement;
movement = Vector3.Zero;
forwardDirection = Vector3.Forward;
isMovingForward = false;
}
}
}

View File

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

View File

@ -0,0 +1,17 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class CharacterMovementType:Resource
{
public virtual void ProcessMovement( CharacterMovementData characterMovementData )
{
}
}
}

View File

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

View File

@ -0,0 +1,78 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class StrafeMovementType:CharacterMovementType
{
public enum StrafeDirectionSource
{
Character_Movement_Direction_Source,
Character_Controller_Body
}
[ExportGroup( "Movement" )]
[Export]
public Sensor forward;
[Export]
public Sensor backwards;
[Export]
public CharacterDirectionProcessing directionProcessing;
[ExportGroup( "Strafing" )]
[Export]
public float strafeSpeedMultiply = 1.0f;
[Export]
public Sensor strafeLeft;
[Export]
public Sensor strafeRight;
[Export]
public StrafeDirectionSource strafeDirectionSource = StrafeDirectionSource.Character_Controller_Body;
public override void ProcessMovement( CharacterMovementData characterMovementData )
{
var characterMovement = characterMovementData.characterMovement;
var directionSource = characterMovement.directionSource;
var body = characterMovement.body;
var dir = directionSource != null ? directionSource : body;
var forwardDirection = dir.GlobalForward();
var rightDirection = StrafeDirectionSource.Character_Controller_Body == strafeDirectionSource ?
body.GlobalRight() : dir.GlobalRight();
if ( CharacterDirectionProcessing.Zero_Y_And_Normalize == directionProcessing )
{
forwardDirection.Y = 0;
rightDirection.Y = 0;
}
else if ( CharacterDirectionProcessing.Project_On_TransformPlane == directionProcessing )
{
var bodyPlane = Plane3.CreateFromNode3D( body );
forwardDirection = bodyPlane.ConstrainToPlane( forwardDirection + body.GlobalPosition ) - body.GlobalPosition;
rightDirection = bodyPlane.ConstrainToPlane( rightDirection + body.GlobalPosition ) - body.GlobalPosition;
}
forwardDirection = forwardDirection.Normalized();
rightDirection = rightDirection.Normalized();
var movement = forwardDirection * Sensors.PolarAxis( backwards, forward ) * characterMovement.moveSpeed;
movement += rightDirection * Sensors.PolarAxis( strafeLeft, strafeRight ) * characterMovement.moveSpeed * strafeSpeedMultiply ;
characterMovementData.movement = movement;
characterMovementData.isMovingForward = forward.isActive;
characterMovementData.forwardDirection = forwardDirection;
}
}
}

View File

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

View File

@ -0,0 +1,105 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TurnMovementType:CharacterMovementType
{
[Export]
public CharacterDirectionProcessing directionProcessing;
[ExportGroup( "Movement" )]
[Export]
public Sensor up;
[Export]
public Sensor down;
[Export]
public Sensor left;
[Export]
public Sensor right;
[ExportGroup( "Strafing" )]
[Export]
public bool useBodyDirection = true;
[Export]
public Sensor strafeBack;
[Export]
public Sensor strafeLeft;
[Export]
public Sensor strafeRight;
[Export]
public float strafeBackSpeedMultiply = 1.0f;
[Export]
public float strafeSidewardsSpeedMultiply = 1.0f;
public override void ProcessMovement( CharacterMovementData characterMovementData )
{
var characterMovement = characterMovementData.characterMovement;
var directionSource = characterMovement.directionSource;
var body = characterMovement.body;
var dir = directionSource != null ? directionSource : body;
var forwardDirection = dir.GlobalForward();
var rightDirection = dir.GlobalRight();
var forwardStrafe = useBodyDirection ? body.GlobalForward() : dir.GlobalForward();
var rightStrafe = useBodyDirection ? body.GlobalRight() : dir.GlobalRight();
if ( CharacterDirectionProcessing.Zero_Y_And_Normalize == directionProcessing )
{
forwardDirection.Y = 0;
rightDirection.Y = 0;
}
else if ( CharacterDirectionProcessing.Project_On_TransformPlane == directionProcessing )
{
var bodyPlane = Plane3.CreateFromNode3D( body );
forwardDirection = bodyPlane.ConstrainToPlane( forwardDirection + body.GlobalPosition ) - body.GlobalPosition;
rightDirection = bodyPlane.ConstrainToPlane( rightDirection + body.GlobalPosition ) - body.GlobalPosition;
}
forwardDirection = forwardDirection.Normalized();
rightDirection = rightDirection.Normalized();
var direction = Sensors.FourDirectional( left, right, up, down );
var movement = forwardDirection * - direction.Y * characterMovement.moveSpeed;
movement += rightDirection * direction.X * characterMovement.moveSpeed;
var turningForwardDirection = movement.Normalized();
if ( Sensors.IsActive( strafeBack ) )
{
movement += -Sensors.GetValue( strafeBack ) * forwardStrafe * characterMovement.moveSpeed * strafeBackSpeedMultiply;
turningForwardDirection = forwardDirection;
}
if ( Sensors.IsActive( strafeLeft ) || Sensors.IsActive( strafeRight ) )
{
var strafeStrength = Sensors.PolarAxis( strafeLeft, strafeRight );
movement += strafeStrength * rightStrafe * characterMovement.moveSpeed * strafeSidewardsSpeedMultiply;
turningForwardDirection = forwardDirection;
}
movement = movement.ConstrainLength( characterMovement.moveSpeed );
characterMovementData.movement = movement;
characterMovementData.isMovingForward = ! Math3D.FacingSameDirection( turningForwardDirection, forwardDirection );
characterMovementData.forwardDirection = turningForwardDirection;
}
}
}

View File

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

View File

@ -5,6 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Gravity:CharacterControllerAction
{

View File

@ -5,6 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GroundReset:CharacterControllerAction
{

View File

@ -5,29 +5,43 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class Jump:CharacterControllerAction
{
[Export]
public Sensor button;
[Export]
public float jumpStrength;
[ExportGroup( "Jump Impulse")]
[Export]
public float maxJumpDuration;
public float jumpImpulseStrength;
[Export( PropertyHint.Range, "0,100" )]
public float velocityToJumpDirection = 0.5f;
[Export]
public Curve jumpCurveStrength = MathX.Curve( 1, 0 );
public float velocityTresholdForJumpDirection = 1f;
[ExportGroup( "Air Control")]
[Export]
public float jumpedDuration;
public Curve airControlCurveStrength = MathX.Curve( 1, 0 );
[Export]
public bool canJump = false;
public float airMaxControlStrength;
[Export]
public bool jumping = false;
public float maxAirControlDuration;
float jumpedDuration;
bool canJump = false;
bool jumping = false;
bool needsToRelease = false;
@ -35,7 +49,11 @@ namespace Rokojori
{
if ( body.IsOnFloor() )
{
jumping = false;
if ( jumping )
{
jumping = false;
needsToRelease = true;
}
}
if ( ! ( jumping || body.IsOnFloor() ) )
@ -46,6 +64,12 @@ namespace Rokojori
if ( ! button.isActive )
{
jumping = false;
needsToRelease = false;
return;
}
if ( needsToRelease )
{
return;
}
@ -53,17 +77,33 @@ namespace Rokojori
{
jumping = true;
jumpedDuration = 0;
var jumpDirection = Vector3.Up * jumpImpulseStrength;
if ( body.Velocity.Length() > velocityTresholdForJumpDirection )
{
var xz = body.Velocity;
xz.Y = 0;
// xz = xz.Normalized();
jumpDirection += xz * velocityToJumpDirection;
}
SetVelocity( jumpDirection );
return;
}
jumpedDuration += controller.delta;
canJump = jumpedDuration < maxJumpDuration;
canJump = jumpedDuration < maxAirControlDuration;
if ( jumpedDuration < maxJumpDuration )
if ( canJump )
{
var jumpStrengthMultiply = jumpCurveStrength.Sample( jumpedDuration / maxJumpDuration );
AddVelocity( Vector3.Up * jumpStrength );
var jumpStrengthMultiply = airControlCurveStrength.Sample( jumpedDuration / maxAirControlDuration );
AddVelocity( Vector3.Up * airMaxControlStrength * jumpStrengthMultiply );
}

View File

@ -5,6 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class MoveAndSlide:CharacterControllerAction
{

View File

@ -21,11 +21,18 @@ namespace Rokojori
[Export]
public Node3D grabOffset;
[Export]
public Smoothing positionSmoothing;
[Export]
public Smoothing rotationSmoothing;
[ExportGroup("Read Only")]
[Export]
public Grabbable grabbable;
public override void _Ready()
{
SensorManager.Register( this, button );
@ -59,7 +66,7 @@ namespace Rokojori
return;
}
var grabbable = Nodes.Find<Grabbable>( pointable.GetParent() );
grabbable = Nodes.Find<Grabbable>( pointable.GetParent() );
if ( grabbable == null )
{
@ -76,6 +83,8 @@ namespace Rokojori
return;
}
positionSmoothing.SetCurrent( grabbable.grabTarget.GlobalPosition );
rotationSmoothing.SetCurrent( grabbable.grabTarget.GetGlobalQuaternion() );
UpdateGrabbable();
}
);
@ -89,7 +98,9 @@ namespace Rokojori
void UpdateGrabbable()
{
// this.LogInfo( "Grabbing", HierarchyName.Of( grabbable ) );
grabbable.grabTarget.GlobalPosition = Smoothing.Apply( positionSmoothing, grabOffset.GlobalPosition, timeLine.delta );
grabbable.grabTarget.SetGlobalQuaternion( Smoothing.Apply( rotationSmoothing, grabOffset.GetGlobalQuaternion(), timeLine.delta ) );
}
}

View File

@ -5,7 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class MultiRayCaster:Caster
{
@ -24,6 +24,9 @@ namespace Rokojori
[Export]
public Vector3 to;
[Export]
public Node collider;
public override int NumColliders()
{
return numCollisions;
@ -59,6 +62,14 @@ namespace Rokojori
ResolveCollisions();
SortCollisions();
var nextCollider = NumColliders() == 0 ? null : GetCollider( 0 );
if ( nextCollider != collider )
{
collider = nextCollider;
// this.LogInfo( "New Collider:", HierarchyName.Of( collider ) );
}
Action.Trigger( afterProcess );

View File

@ -5,7 +5,7 @@ using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/Pointer.svg")]
public partial class Pointer:Node3D
{

View File

@ -18,6 +18,11 @@ namespace Rokojori
return LookingAtEachOtherAngle( lookDirectionA, lookDirectionB ) > 0;
}
public static bool FacingSameDirection( Vector3 lookDirectionA, Vector3 lookDirectionB )
{
return ! LookingAtEachOther( lookDirectionA, lookDirectionB );
}
public static bool LookingTowards( Vector3 from, Vector3 fromDirection, Vector3 to )
{
return LookingAtEachOther( fromDirection, to - from );
@ -103,11 +108,26 @@ namespace Rokojori
return v.X == 0 && v.Y == 0 && v.Z == 0;
}
public static Vector3 ConstrainLength( this Vector3 v, float maxLength )
{
var length = v.Length();
if ( length <= maxLength )
{
return v;
}
return v.Normalized() * maxLength;
}
public static Vector3 ComputeNormalFast( Vector3 a, Vector3 b, Vector3 c )
{
return Math3D.Cross( c - b , a - b ).Normalized();
}
public static Vector3 ComputeNormal( Vector3 a, Vector3 b, Vector3 c )
{
var cross = Math3D.Cross( c - b , a - b );
@ -440,7 +460,12 @@ namespace Rokojori
public static float GlobalYaw( Vector3 direction )
{
return Mathf.Atan2( direction.Z, direction.X );
return ( Mathf.Pi * 2.0f - Mathf.Atan2( direction.Z, direction.X ) ) - Mathf.Pi / 2.0f;
}
public static float GlobalYawDegrees( Vector3 direction )
{
return Mathf.RadToDeg( GlobalYaw( direction ) );
}
public static float GlobalPitch( Vector3 direction )
@ -451,6 +476,11 @@ namespace Rokojori
return Mathf.Atan2( y, xz );
}
public static float GlobalPitchDegrees( Vector3 direction )
{
return Mathf.RadToDeg( GlobalPitch( direction ) );
}
public static void SetGlobalQuaternion( this Node3D node, Quaternion quaternion )
{
var localScale = node.Scale;
@ -620,6 +650,38 @@ namespace Rokojori
node.GlobalPosition = gp;
}
public static void SetLocalX( this Node3D node, float x )
{
var gp = node.Position;
gp.X = x;
node.Position = gp;
}
public static void SetLocalY( this Node3D node, float y )
{
var gp = node.Position;
gp.Y = y;
node.Position = gp;
}
public static void SetLocalZ( this Node3D node, float z )
{
var gp = node.Position;
gp.Z = z;
node.Position = gp;
}
public static Vector3 GetOrientationBasedGlobalOffset( this Node3D node, Vector3 offset )
{
return offset.X * node.GlobalRight() + offset.Y * node.GlobalUp() + offset.Z * node.GlobalForward();
}
public static Vector3 Average( List<Vector3> vectors )
{
var average = Vector3.Zero;

View File

@ -96,6 +96,21 @@ namespace Rokojori
return Mathf.Abs( AngleDelta( degreesA, degreesB ) );
}
public static float NormalizeAngle( float degrees )
{
while ( degrees > 180 )
{
degrees -= 360;
}
while ( degrees < -180 )
{
degrees += 360;
}
return degrees;
}
public static float Smoothstep ( float edge0, float edge1, float x )
{
x = MathX.Clamp01( ( x - edge0 ) / ( edge1 - edge0 ) );
@ -118,6 +133,17 @@ namespace Rokojori
return Mathf.Floor( value / snappingDistance ) * snappingDistance;
}
public static Vector2 RadiansToVector2( float angle )
{
return new Vector2( Mathf.Cos( angle ), Mathf.Sin( angle ) );
}
public static float Vector2ToRadians( Vector2 circle )
{
return Mathf.Atan2( circle.Y, circle.X );
}
public const float DegreesToRadians = Mathf.Pi / 180f;
public const float RadiansToDegrees = 180f / Mathf.Pi;

View File

@ -27,23 +27,9 @@ namespace Rokojori
[Export]
public MeshInstance3D output;
[Export]
public bool update = false;
[Export]
public bool updateAlways = false;
public override void _Process( double delta )
{
if ( ! ( update || updateAlways ) )
{
return;
}
update = false;
Create();
}
[ExportToolButton( "Update")]
public Callable UpdateButton => Callable.From( () => Create() );
public float X()
@ -148,6 +134,11 @@ namespace Rokojori
mg.AddQuad( 20, 21, 22, 23, true );
if ( output == null )
{
output = this.CreateChild<MeshInstance3D>();
}
output.Mesh = mg.GenerateMesh();
}

View File

@ -1,10 +1,10 @@
[gd_resource type="Resource" script_class="GamePadAxisSensor" load_steps=2 format=3 uid="uid://dbha8dmhxgm05"]
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadAxisSensor.cs" id="1_yy4wi"]
[ext_resource type="Script" uid="uid://cb81s7ud1de7h" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadAxisSensor.cs" id="1_yy4wi"]
[resource]
script = ExtResource("1_yy4wi")
axis = 0
axis = 1
type = 1
continous = true
_value = 0.0

View File

@ -0,0 +1,9 @@
[gd_resource type="Resource" script_class="GamePadAxisSensor" load_steps=2 format=3 uid="uid://ck7woerh7mhp"]
[ext_resource type="Script" uid="uid://cb81s7ud1de7h" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadAxisSensor.cs" id="1_nrdau"]
[resource]
script = ExtResource("1_nrdau")
axis = 3
type = 1
continous = true

View File

@ -1,13 +0,0 @@
[gd_resource type="Resource" script_class="GamePadAxisSensor" load_steps=2 format=3 uid="uid://ck7woerh7mhp"]
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadAxisSensor.cs" id="1_nrdau"]
[resource]
script = ExtResource("1_nrdau")
axis = 3
type = 1
continous = true
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -1,12 +1,8 @@
[gd_resource type="Resource" script_class="GamePadButtonSensor" load_steps=2 format=3 uid="uid://jvwwq6guhl77"]
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadButtonSensor.cs" id="1_4da72"]
[ext_resource type="Script" uid="uid://0ji11kv86cpk" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadButtonSensor.cs" id="1_4da72"]
[resource]
script = ExtResource("1_4da72")
button = 1
button = 2
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -1,12 +1,8 @@
[gd_resource type="Resource" script_class="GamePadButtonSensor" load_steps=2 format=3 uid="uid://dpwq5eyla8rob"]
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadButtonSensor.cs" id="1_yv1r0"]
[ext_resource type="Script" uid="uid://0ji11kv86cpk" path="res://addons/rokojori_action_library/Runtime/Sensors/GamePadButtonSensor.cs" id="1_yv1r0"]
[resource]
script = ExtResource("1_yv1r0")
button = 2
button = 3
continous = false
_value = 0.0
_wasActive = false
_active = false
_activeTreshold = 0.5

View File

@ -1,14 +1,14 @@
[gd_resource type="Resource" script_class="SensorGroup" load_steps=28 format=3 uid="uid://bv40lrpi3831d"]
[ext_resource type="Script" path="res://addons/rokojori_action_library/Runtime/Sensors/SensorGroup.cs" id="1_k6ypa"]
[ext_resource type="Resource" uid="uid://5gnh5dmv1p21" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick X-Negative.tres" id="2_1ywmr"]
[ext_resource type="Resource" uid="uid://dsrf03g6mgu5t" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick X-Positive.tres" id="3_1yuu5"]
[ext_resource type="Resource" uid="uid://dbha8dmhxgm05" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick Y-Negative.tres" id="4_ehig4"]
[ext_resource type="Resource" uid="uid://cyyy0ycusgil3" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick Y-Positive.tres" id="5_adyq7"]
[ext_resource type="Resource" uid="uid://b16mtcrpm1f6i" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick X-Negative.tres" id="6_g7fxo"]
[ext_resource type="Resource" uid="uid://d05w143o644d3" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick X-Positive.tres" id="7_b0v3q"]
[ext_resource type="Resource" uid="uid://ck7woerh7mhp" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick Y-Negative.tres" id="8_ljqng"]
[ext_resource type="Resource" uid="uid://6emg8n3qxhlv" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick Y-Positive.tres" id="9_qkgw5"]
[ext_resource type="Script" uid="uid://da4bhmvkury2" path="res://addons/rokojori_action_library/Runtime/Sensors/SensorGroup.cs" id="1_k6ypa"]
[ext_resource type="Resource" uid="uid://5gnh5dmv1p21" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick Left -X.tres" id="2_1ywmr"]
[ext_resource type="Resource" uid="uid://dsrf03g6mgu5t" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick Right +X.tres" id="3_1yuu5"]
[ext_resource type="Resource" uid="uid://dbha8dmhxgm05" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick Up -Y.tres" id="4_ehig4"]
[ext_resource type="Resource" uid="uid://cyyy0ycusgil3" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Left-Joystick Down +Y.tres" id="5_adyq7"]
[ext_resource type="Resource" uid="uid://b16mtcrpm1f6i" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick Left -X.tres" id="6_g7fxo"]
[ext_resource type="Resource" uid="uid://d05w143o644d3" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick Right +X.tres" id="7_b0v3q"]
[ext_resource type="Resource" uid="uid://ck7woerh7mhp" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick Up -Y.tres" id="8_ljqng"]
[ext_resource type="Resource" uid="uid://6emg8n3qxhlv" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Right-Joystick Down +Y.tres" id="9_qkgw5"]
[ext_resource type="Resource" uid="uid://cs12bjge707ri" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Trigger Left, LT, PS L2, Nin ZL.tres" id="10_t2i3f"]
[ext_resource type="Resource" uid="uid://cy3ja8squnin6" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Axis/Trigger Right, RT, PS R2, Nin ZR.tres" id="11_u6ig2"]
[ext_resource type="Resource" uid="uid://dffkdky8iowro" path="res://addons/rokojori_action_library/Runtime/Sensors/Default-Sensors/Gamepad/Buttons/Button A, PS Cross, Nin B.tres" id="12_0r6yt"]

View File

@ -0,0 +1,75 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/SensorGroup.svg")]
public partial class HoldSensor : Sensor
{
[Export]
public Sensor holdButton;
[Export]
public bool negateHoldButton;
[Export]
public Sensor triggerButton;
protected override void UpdateValue()
{
if ( holdButton == null || triggerButton == null )
{
SetFloatValue( 0 );
return;
}
var holdButtonValue = holdButton.isActive;
if ( negateHoldButton )
{
holdButtonValue = ! holdButtonValue;
}
if ( ! holdButtonValue )
{
SetFloatValue( 0 );
return;
}
SetFloatValue( triggerButton.value );
}
public override string ToString()
{
return RJLog.GetInfo( this, holdButton, triggerButton );
}
[Export]
public InputIcon[] inputIcons = [];
[Export]
public bool useInputIconsFromSensors = true;
public override List<InputIcon> GetInputIcons()
{
var list = Lists.From( inputIcons );
if ( useInputIconsFromSensors )
{
if ( holdButton != null )
{
list.AddRange( holdButton.GetInputIcons() );
}
if ( triggerButton != null )
{
list.AddRange( triggerButton.GetInputIcons() );
}
}
return list;
}
}
}

View File

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

View File

@ -17,18 +17,13 @@ namespace Rokojori
[Export]
public bool continous = false;
[ExportGroup("Read Only")]
[Export]
public float _value = 0;
protected float _value = 0;
[Export]
public bool _wasActive = false;
protected bool _wasActive = false;
[Export]
public bool _active = false;
protected bool _active = false;
[Export]
public float _activeTreshold = 0.5f;
protected float _activeTreshold = 0.5f;
public void ProcessSensor( SensorRunner runner, float delta )
{

View File

@ -35,16 +35,22 @@ namespace Rokojori
[ExportGroup("Read Only")]
[Export]
public SensorDevice[] deviceList = new SensorDevice[]{};
public SensorDevice[] deviceList =[];
[Export]
public float[] deviceLastInputTimeStamp = new float[]{};
public float[] deviceLastInputTimeStamp = [];
[ExportGroup("Testing")]
[Export]
public SensorDevice testingLastActiveDevice;
[Export]
public bool showRegistratedSensors = true;
[Export]
public Sensor[] registratedSensors = [];
@ -61,6 +67,12 @@ namespace Rokojori
DateTime _startTime;
public bool isMouseKeyboardLastActive => lastActiveDevice == mouseKeyboardDevice ||
lastActiveDevice == mouseDevice ||
lastActiveDevice == keyboardDevice;
public bool isControllerLastActive => lastActiveDevice == gamePadDevice;
public SensorDevice lastActiveDevice
{
get
@ -175,8 +187,11 @@ namespace Rokojori
runners.ForEach( r => r.Update( (float) delta ) );
}
public void Register( Sensor s, SensorInputHandler sih )
{
this.LogInfo( "Registrating", s );
var sensorRunner = sensorToRunner[ s ];
if ( sensorRunner.listeners.Contains( sih ) )
@ -185,6 +200,8 @@ namespace Rokojori
}
sensorRunner.listeners.Add( sih );
}
public void Unregister( Sensor s, SensorInputHandler sih )
@ -197,6 +214,11 @@ namespace Rokojori
{
var sm = Unique<SensorManager>.Get();
if ( sm == null )
{
Nodes.ForEachInScene<Node>( n => n.LogInfo() );
}
foreach ( var s in sensors )
{
if ( s == null )
@ -241,6 +263,11 @@ namespace Rokojori
sensorsSet.Add( s );
runners.Add( new SensorRunner( s ) );
if ( showRegistratedSensors )
{
registratedSensors = Arrays.Add( registratedSensors, s );
}
}
HashSet<object> _scannedObjects = new HashSet<object>();
@ -257,6 +284,19 @@ namespace Rokojori
var sensors = ReflectionHelper.GetDataMemberValues<Sensor>( obj );
if ( sensors.Count > 0 )
{
if ( obj is Node n )
{
n.LogInfo( sensors );
}
else if ( obj is Resource r )
{
r.LogInfo( sensors );
}
}
sensors.ForEach(
s =>
{
@ -309,12 +349,12 @@ namespace Rokojori
foreach ( var n in autoScanForSensors )
{
Nodes.ForEach<Node>( n, cn=> AddSensorsFrom( cn ) );
Nodes.ForEach<Node>( n, AddSensorsFrom );
}
if ( autoScanParent )
{
Nodes.ForEach<Node>( GetParent(), cn=> AddSensorsFrom( cn ) );
Nodes.ForEach<Node>( GetParent(), AddSensorsFrom );
}
runners.ForEach(

View File

@ -0,0 +1,32 @@
using Godot;
using System.Collections.Generic;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/SensorManager.svg")]
public partial class SensorViewer: Node
{
[Export]
public Sensor sensor;
[Export]
public float value;
[Export]
public bool active;
public override void _Process(double delta)
{
if ( sensor == null )
{
return;
}
value = sensor.value;
active = sensor.isActive;
}
}
}

View File

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

View File

@ -34,5 +34,23 @@ namespace Rokojori
{
return GetValue( negative, -scale, deadZone ) + GetValue( positive, scale, deadZone );
}
public static Vector2 FourDirectional( Sensor left, Sensor right, Sensor up, Sensor down, bool normalize = true, float deadZone = 0.3f )
{
var x = PolarAxis( left, right, 1, deadZone );
var y = PolarAxis( up, down, 1, deadZone );
var v = new Vector2( x, y );
if ( normalize && v.Length() > 1 )
{
v = v.Normalized();
}
return v;
}
}
}

View File

@ -15,6 +15,8 @@ namespace Rokojori
public override void _Ready()
{
this.LogInfo( "Root" );
SensorManager.Register( this, sensor );
}

View File

@ -73,3 +73,18 @@ float angleDelta( float degreesA, float degreesB )
angleDelta = mod( angleDelta + 180.0, 360.0 ) - 180.0;
return angleDelta;
}
float snapRounded( float value, float snappingDistance )
{
return round( value / snappingDistance ) * snappingDistance;
}
float snapCeiled( float value, float snappingDistance )
{
return ceil( value / snappingDistance ) * snappingDistance;
}
float snapFloored( float value, float snappingDistance )
{
return floor( value / snappingDistance ) * snappingDistance;
}

View File

@ -1,5 +1,7 @@
// #include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc"
#include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc"
float random( vec2 uv )
{
return fract( sin( dot( uv.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453123 );
@ -7,9 +9,16 @@ float random( vec2 uv )
vec2 random_v2( vec2 uv )
{
return vec2( fract( sin( dot( uv.xy, vec2( 12.9898,78.233 ) ) ) * 43758.5453123 ) );
uv = vec2
(
dot(uv, vec2( 127.1,311.7 ) ),
dot(uv, vec2( 269.5,183.3 ) )
);
return -1.0 + 2.0 * fract( sin( uv ) * 43758.5453123 );
}
vec3 random_v3( vec3 uvw )
{
@ -20,7 +29,20 @@ vec3 random_v3( vec3 uvw )
return -1.0 + 2.0 * fract(sin(uvw) * 43758.5453123);
}
float perlin( vec2 uv )
// float perlin( vec2 uv )
// {
// vec2 uv_index = floor(uv);
// vec2 uv_fract = fract(uv);
// vec2 blur = smoothstep(0.0, 1.0, uv_fract);
// return mix( mix( dot( random_v2(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ),
// dot( random_v2(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x),
// mix( dot( random_v2(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ),
// dot( random_v2(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) + 0.5;
// }
float perlin(vec2 uv)
{
vec2 uv_index = floor(uv);
vec2 uv_fract = fract(uv);
@ -33,6 +55,61 @@ float perlin( vec2 uv )
dot( random_v2(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) + 0.5;
}
float perlinOctaves( vec2 uv, int octaves, float scale, float gain )
{
float s = 1.0;
float g = 1.0;
float v = perlin( uv * s ) * g;
float n = 1.0;
for ( int i = 0; i < octaves; i++ )
{
s *= scale;
g *= gain;
v += perlin( uv * s ) * g;
n += g;
}
return v / n;
}
vec2 seamLessCoordinate( vec2 uv, vec2 seamRange, vec2 seamFade, vec2 type )
{
return mod( uv - seamFade * type, seamRange );
}
float perlinOctavesSeamless( vec2 uv, int octaves, float scale, float gain, vec2 seamRange, vec2 seamFade )
{
// uv = mod( uv , seamRange );
seamFade *= seamRange;
vec2 fading = mod( uv, seamRange ) ;
fading.x = normalizeToRange01( fading.x, seamRange.x - seamFade.x, seamRange.x );
fading.y = normalizeToRange01( fading.y, seamRange.y - seamFade.y, seamRange.y );
// 0, seamRange - seamFade, seamRange
//
vec2 p00 = seamLessCoordinate( uv, seamRange, seamFade, vec2( 0.0, 0.0 ) );
vec2 p10 = seamLessCoordinate( uv, seamRange, seamFade, vec2( 1.0, 0.0 ) );
vec2 p01 = seamLessCoordinate( uv, seamRange, seamFade, vec2( 0.0, 1.0 ) );
vec2 p11 = seamLessCoordinate( uv, seamRange, seamFade, vec2( 1.0, 1.0 ) );
float n00 = perlinOctaves( p00, octaves, scale, gain );
float n10 = perlinOctaves( p10, octaves, scale, gain );
float n01 = perlinOctaves( p01, octaves, scale, gain );
float n11 = perlinOctaves( p11, octaves, scale, gain );
float n0 = mix( n00, n10, fading.x );
float n1 = mix( n01, n11, fading.x );
return mix( n0, n1, fading.y );
}
float perlin3D( vec3 uvw )
{
vec3 gridIndex = floor( uvw );

View File

@ -48,6 +48,7 @@ namespace Rokojori
high = tweened;
}
if ( validateLimits != null && ! validateLimits( low, high ) )
{
throw new Exception( "Limit validation failed" );

View File

@ -39,6 +39,20 @@ namespace Rokojori
return last;
}
public static async Task<double> WaitIfExceeded( double last, Node node, double waitTime = Async.waitTime )
{
var now = Time.GetTicksMsec() / 1000.0;
if ( ( now - last ) > waitTime )
{
await node.RequestNextFrame();
return Time.GetTicksMsec() / 1000.0;;
}
return last;
}
public static double DoIfExceeded( double last, System.Action action, double waitTime = Async.waitTime )
{
var now = Time.GetTicksMsec() / 1000.0;

View File

@ -8,7 +8,7 @@ namespace Rokojori
{
public class Safe
{
static readonly int maxWhileIterations = 2000 * 1000;
static readonly int maxWhileIterations = 1000 * 1000;
public static void While( Func<bool> condition, System.Action action, System.Action onTooManyIterations = null )
{

View File

@ -0,0 +1,53 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class CameraTargetOffset:Node3D
{
[Export]
public float cameraTargetMinOffsetZ = 0;
[Export]
public float cameraTargetMaxOffsetZ = 10;
[Export]
public Smoothing cameraTargetOffsetZSmoothing = new FrameSmoothing();
[Export]
public float cameraTargetMinOffsetY = 1.7f;
[Export]
public float cameraTargetMaxOffsetY = 2f;
[Export]
public Smoothing cameraTargetOffsetYSmoothing = new FrameSmoothing();
protected float nextZ = 0;
protected float nextY = 0;
public virtual void SetMovingForward( float movingForward )
{
nextZ = Mathf.Lerp( cameraTargetMinOffsetZ, cameraTargetMaxOffsetZ, movingForward );
nextY = Mathf.Lerp( cameraTargetMinOffsetY, cameraTargetMaxOffsetY, movingForward );
}
public override void _Process( double delta )
{
var smoothedOffsetZ = cameraTargetOffsetZSmoothing.Smooth( nextZ, (float)delta );
this.SetLocalZ( smoothedOffsetZ );
var smoothedOffsetY = cameraTargetOffsetYSmoothing.Smooth( nextY, (float)delta );
this.SetLocalY( smoothedOffsetY );
}
}
}

View File

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

View File

@ -15,6 +15,9 @@ namespace Rokojori
[Export]
public Node3D target;
[Export]
public Smoothing targetFollowSmoothing = new FrameSmoothing();
[ExportGroup("Yaw")]
[Export]
@ -26,14 +29,39 @@ namespace Rokojori
[Export]
public Sensor yawNegativeAxis;
[Export]
public bool roundYaw = false;
[Export]
public int roundedYawResolution = 64;
[Export]
public float yaw = 0;
[Export]
public float yawSmoothingDuration = 0.1f;
public Smoothing yawSmoothing = new FrameSmoothing();
[Export]
public bool yawGoesBehindPlayer = true;
[Export]
public Smoothing yawToBehingSmoothing = new FrameSmoothing();
[Export]
public float yawBehindDelayDuration = 5;
[Export]
public float yawGoesBehindActivation = 0.1f;
[Export]
public float behindYaw = 0;
[Export]
public float currentYaw = 0;
[Export]
public float bcDelta = 0;
Smoother yawSmoother = new Smoother();
[ExportGroup("Pitch")]
@ -58,27 +86,44 @@ namespace Rokojori
[Export]
public float maxPitch = 80;
public float normalizedPitch => MathX.NormalizeClamped( pitch, minPitch, maxPitch );
[Export]
public float pitchSmoothingDuration = 0.1f;
public Smoothing pitchSmoothing = new FrameSmoothing();
Smoother pitchSmoother = new Smoother();
[Export]
public bool pitchGoesBackToCenter = true;
[Export]
public Smoothing toCenterPitchSmoothing = new FrameSmoothing();
[Export]
public float centerPitchDelayDuration = 5;
[Export]
public float centerPitch = 30;
[Export]
public float centerPitchActivation = 0.1f;
[ExportGroup("Distance")]
[Export]
public Curve distanceForPitch = MathX.Curve( 1, 1 );
[Export]
public float distanceScale = 1;
[ExportGroup("Offset")]
[ExportGroup("Camera Offset")]
[Export]
public Vector3 offset;
Smoother distanceSmoother = new Smoother();
float smoothedYaw = 0;
float smoothedPitch = 0;
float smoothedDistance = 0;
float _centerPitchActivation = 0;
float _yawBehindActivation = 0;
public override void _Process( double delta )
{
@ -87,17 +132,60 @@ namespace Rokojori
return;
}
behindYaw = Math3D.GlobalYawDegrees( target.GlobalForward() * -1 );
behindYaw = MathX.NormalizeAngle( behindYaw );
currentYaw = MathX.NormalizeAngle( yaw );
bcDelta = MathX.NormalizeAngle( MathX.AngleDelta( behindYaw, currentYaw ) );
var targetPosition = Smoothing.Apply( targetFollowSmoothing, target.GlobalPosition, (float) delta );
var yawAxis = Sensors.PolarAxis( yawNegativeAxis, yawPositiveAxis );
var pitchAxis = Sensors.PolarAxis( pitchNegativeAxis, pitchPositiveAxis );
yaw += yawAxis * yawSpeed * (float)delta;
if ( yawGoesBehindPlayer )
{
if ( Mathf.Abs( yawAxis ) < yawGoesBehindActivation )
{
_yawBehindActivation += (float)delta;
if ( yawGoesBehindPlayer && _yawBehindActivation > yawBehindDelayDuration )
{
yaw = Smoothing.ApplyDegreesWith( yawToBehingSmoothing, yaw, behindYaw, (float) delta );
}
}
else
{
_yawBehindActivation = 0;
}
}
// yaw = MathX.Repeat( yaw, 360f );
if ( pitchIsRelative )
{
pitch += pitchAxis * pitchSpeed * (float)delta;
pitch = Mathf.Clamp( pitch, minPitch, maxPitch );
if ( Mathf.Abs( pitchAxis ) < centerPitchActivation )
{
_centerPitchActivation += (float)delta;
if ( _centerPitchActivation > centerPitchDelayDuration )
{
pitch = Smoothing.ApplyWith( toCenterPitchSmoothing, pitch, centerPitch, (float) delta );
}
}
else
{
_centerPitchActivation = 0;
}
}
else
{
@ -105,20 +193,36 @@ namespace Rokojori
}
if ( Mathf.Abs( yaw - smoothedYaw ) > 180 )
{
if ( yaw > smoothedYaw )
{
smoothedYaw += 360;
}
else if ( yaw < smoothedYaw )
{
smoothedYaw -= 360;
}
}
smoothedYaw = yaw;
smoothedPitch = pitchSmoother.SmoothForDuration( smoothedPitch, pitch, pitchSmoothingDuration, (float) delta );
// if ( Mathf.Abs( yaw - smoothedYaw ) > 180 )
// {
// if ( yaw > smoothedYaw )
// {
// smoothedYaw += 360;
// }
// else if ( yaw < smoothedYaw )
// {
// smoothedYaw -= 360;
// }
// }
var appliedYaw = yaw;
// if ( roundYaw )
// {
// appliedYaw = MathX.SnapRounded( yaw, 360f/roundedYawResolution );
// }
smoothedYaw = Smoothing.ApplyDegrees( yawSmoothing, appliedYaw, (float) delta );
smoothedPitch = Smoothing.Apply( pitchSmoothing, pitch, (float) delta );
// if ( pitchGoesBackToCenter )
// {
// pitch = toCenterPitchSmoothing.Smooth( centerPitch, (float) delta );
// }
// RJLog.Log( "Pitch", smoothedPitch );
@ -126,11 +230,11 @@ namespace Rokojori
// smoothedPitch = pitch;
var distance = distanceForPitch.Sample( MathX.NormalizeClamped( pitch, minPitch, maxPitch ) ) * distanceScale;
GlobalPosition = target.GlobalPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance + offset;
LookAt( target.GlobalPosition + offset, Vector3.Up, true );
GlobalPosition = targetPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance;
LookAt( targetPosition, Vector3.Up, true );
GlobalPosition += this.GetOrientationBasedGlobalOffset( offset );
}

View File

@ -0,0 +1,39 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class ThirdPersonCameraTargetOffset:CameraTargetOffset
{
[Export]
public ThirdPersonCamera thirdPersonCamera;
[Export]
public Curve pitchMultiply;
public override void SetMovingForward( float movingForward )
{
if ( pitchMultiply != null && thirdPersonCamera != null )
{
var normalizedPitch = thirdPersonCamera.normalizedPitch;
var sampledPitchMultiply = pitchMultiply.Sample( normalizedPitch );
movingForward *= sampledPitchMultiply;
}
nextZ = Mathf.Lerp( cameraTargetMinOffsetZ, cameraTargetMaxOffsetZ, movingForward );
nextY = Mathf.Lerp( cameraTargetMinOffsetY, cameraTargetMaxOffsetY, movingForward );
}
}
}

View File

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

View File

@ -21,6 +21,7 @@ namespace Rokojori
[Export]
public bool active = false;
public static VirtualCamera3DManager Get()
{
return Unique<VirtualCamera3DManager>.Get();