Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/Engine/Plugins/Runtime/CableComponent/CableComponent.uplugin b/Engine/Plugins/Runtime/CableComponent/CableComponent.uplugin
- index eb42d2d..99508ab 100644
- --- a/Engine/Plugins/Runtime/CableComponent/CableComponent.uplugin
- +++ b/Engine/Plugins/Runtime/CableComponent/CableComponent.uplugin
- @@ -3,9 +3,9 @@
- "FriendlyName" : "CableComponent Plugin",
- "Version" : 1,
- - "VersionName" : "1.0",
- - "CreatedBy" : "Epic Games, Inc.",
- - "CreatedByURL" : "http://epicgames.com",
- + "VersionName" : "1.1",
- + "CreatedBy" : "Epic Games, Inc.; Vitaly Varivdin",
- + "CreatedByURL" : "http://epicgames.com; vitaly.varivdin@gmail.com",
- "MinEngineVersion" : 1579795,
- "Description" : "A simulated cable component.",
- "CategoryPath" : "Programming Rendering.Plugins",
- diff --git a/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Classes/CableComponent.h b/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Classes/CableComponent.h
- index 169daea..1352243 100644
- --- a/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Classes/CableComponent.h
- +++ b/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Classes/CableComponent.h
- @@ -4,24 +4,87 @@
- #include "CableComponent.generated.h"
- +UENUM()
- +namespace ECableParticleState
- +{
- + enum Type
- + {
- + Free,
- + Fixed,
- + Attached
- + };
- +}
- +
- /** Struct containing information about a point along the cable */
- +USTRUCT()
- struct FCableParticle
- {
- + GENERATED_USTRUCT_BODY()
- +
- FCableParticle()
- - : bFree(true)
- - , Position(0,0,0)
- - , OldPosition(0,0,0)
- + : State(ECableParticleState::Free),
- + Position(0, 0, 0),
- + OldPosition(0, 0, 0),
- + Acceleration(0, 0, 0),
- + Mass(1.0f),
- + InverseMass(1.0f)
- {}
- - /** If this point is free (simulating) or fixed to something */
- - bool bFree;
- + UPROPERTY(EditAnywhere, Category = "Particle")
- + TEnumAsByte<ECableParticleState::Type> State;
- +
- /** Current position of point */
- + UPROPERTY(EditAnywhere, Category = "Particle")
- FVector Position;
- /** Position of point on previous iteration */
- + UPROPERTY(EditAnywhere, Category = "Particle")
- FVector OldPosition;
- + /** Acceleration. Accumulated each tick */
- + UPROPERTY(EditAnywhere, Category = "Particle")
- + FVector Acceleration;
- +
- + /** Mass of the particle */
- + UPROPERTY(EditAnywhere, Category = "Particle")
- + float Mass;
- + UPROPERTY(EditAnywhere, Category = "Particle")
- + float InverseMass;
- +
- + /** Defines actor component if particle is attached to object */
- + UPROPERTY(EditAnywhere, Category = "Particle")
- + FComponentReference AttachedTo;
- +
- + void VerletIntegrate(float InSubstepTime, const FVector& Gravity);
- +
- + void ApplyForce(FVector Force);
- +};
- +
- +USTRUCT()
- +struct FCableParticlesConstraint
- +{
- + GENERATED_USTRUCT_BODY()
- +
- + FCableParticlesConstraint()
- + : RestLength(50.0f),
- + BreakLengthFactor(1.2f),
- + BreakLength(60.0f),
- + Stiffness(1.0f)
- + {}
- +
- + FCableParticle* ParticleA;
- + FCableParticle* ParticleB;
- +
- + UPROPERTY(EditAnywhere, Category = "Constraint")
- + float RestLength;
- + UPROPERTY(EditAnywhere, Category = "Constraint")
- + float BreakLengthFactor;
- + UPROPERTY(EditAnywhere, Category = "Constraint")
- + float BreakLength;
- + UPROPERTY(EditAnywhere, Category = "Constraint")
- + float Stiffness;
- +
- + void Solve();
- };
- -/** Component that allows you to specify custom triangle mesh geometry */
- UCLASS(hidecategories=(Object, LOD, Physics, Collision, Activation, "Components|Activation"), editinlinenew, meta=(BlueprintSpawnableComponent), ClassGroup=Rendering)
- class UCableComponent : public UMeshComponent
- {
- @@ -47,31 +110,31 @@ public:
- virtual int32 GetNumMaterials() const OVERRIDE;
- // End UMeshComponent interface.
- -
- - /** Actor or Component that the end of the cable should be attached to */
- - UPROPERTY(EditAnywhere, Category="Cable")
- - FComponentReference AttachEndTo;
- -
- - /** End location of cable, relative to AttachEndTo if specified, otherwise relative to cable component. */
- - UPROPERTY(EditAnywhere, Category="Cable")
- - FVector EndLocation;
- + /** Simulation properties */
- /** Rest length of the cable */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Cable", meta=(ClampMin = "0.0", UIMin = "0.0", UIMax = "1000.0"))
- float CableLength;
- /** How many segments the cable has */
- - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Cable", meta=(ClampMin = "1", UIMin = "1", UIMax = "20"))
- + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Cable", meta=(ClampMin = "1", UIMin = "1", UIMax = "50"))
- int32 NumSegments;
- - /** Controls the simulation substep time for the cable */
- - UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadOnly, Category="Cable", meta=(ClampMin = "0.005", UIMin = "0.005", UIMax = "0.1"))
- - float SubstepTime;
- + UPROPERTY(EditAnywhere, Category="Cable", meta=(ClampMin = "0.0", UIMin = "0.0", UIMax = "100.0"))
- + float ParticleMass;
- +
- + UPROPERTY(EditAnywhere, Category="Cable", meta=(ClampMin = "0.0", UIMin = "0.1", UIMax = "1.0"))
- + float ConstraintStiffness;
- /** The number of solver iterations controls how 'stiff' the cable is */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Cable", meta=(ClampMin = "1", ClampMax = "8"))
- int32 SolverIterations;
- + /** Controls the simulation substep time for the cable */
- + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadOnly, Category = "Cable", meta = (ClampMin = "0.005", UIMin = "0.005", UIMax = "0.1"))
- + float SubstepTime;
- +
- + /** Rendering properties */
- /** How wide the cable geometry is */
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Cable Rendering", meta=(ClampMin = "0.01", UIMin = "0.01", UIMax = "50.0"))
- @@ -87,18 +150,23 @@ public:
- private:
- - /** Solve the cable spring constraints */
- - void SolveConstraints();
- - /** Integrate cable point positions */
- - void VerletIntegrate(float SubstepTime, const FVector& Gravity);
- /** Perform a simulation substep */
- void PerformSubstep(float SubstepTime, const FVector& Gravity);
- /** Get start and end position for the cable */
- - void GetEndPositions(FVector& OutStartPosition, FVector& OutEndPosition);
- + FVector GetStartPosition();
- /** Amount of time 'left over' from last tick */
- float TimeRemainder;
- +
- /** Array of cable particles */
- - TArray<FCableParticle> Particles;
- + UPROPERTY(EditAnywhere, Category = "Cable")
- + TArray<FCableParticle> Particles;
- + /** Array of particle constraints */
- + UPROPERTY(EditAnywhere, Category = "Cable")
- + TArray<FCableParticlesConstraint> Constraints;
- +
- + /** Stored particle states for restoring before renegeration.
- + * Allows to change particle state in editor. There might be a better way i guess. */
- + TMap<int, ECableParticleState::Type> States;
- friend class FCableSceneProxy;
- diff --git a/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Private/CableComponent.cpp b/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Private/CableComponent.cpp
- index 0d2df89..3d15135 100644
- --- a/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Private/CableComponent.cpp
- +++ b/Engine/Plugins/Runtime/CableComponent/Source/CableComponent/Private/CableComponent.cpp
- @@ -312,27 +312,77 @@ private:
- float TileMaterial;
- };
- +void FCableParticle::VerletIntegrate(float InSubstepTime, const FVector& Gravity)
- +{
- + const float SubstepTimeSqr = InSubstepTime * InSubstepTime;
- +
- + if(State != ECableParticleState::Fixed)
- + {
- + ApplyForce(Mass * Gravity);
- +
- + const FVector Vel = Position - OldPosition;
- + const FVector NewPosition = Position + Vel + (Acceleration * SubstepTimeSqr);
- +
- + OldPosition = Position;
- + Position = NewPosition;
- + Acceleration = FVector(0, 0, 0);
- + }
- +}
- +void FCableParticle::ApplyForce(FVector Force)
- +{
- + if(State != ECableParticleState::Fixed)
- + {
- + // Acceleration += 1 / Mass * Force;
- + Acceleration += Force / Mass;
- + }
- +}
- +
- +void FCableParticlesConstraint::Solve()
- +{
- + // Find current vector between particles
- + FVector Delta = ParticleB->Position - ParticleA->Position;
- +
- + float CurrentDistance = Delta.Size();
- + float ErrorFactor = (CurrentDistance - RestLength) / CurrentDistance;
- +
- + float ScalarA = (ParticleA->InverseMass / (ParticleA->InverseMass + ParticleB->InverseMass)) * Stiffness;
- + float ScalarB = Stiffness - ScalarA;
- +
- + // Only move free or attached particles to satisfy constraints
- + if(ParticleA->State != ECableParticleState::Fixed)
- + {
- + ParticleA->Position += ScalarA * ErrorFactor * Delta;
- + }
- +
- + if(ParticleB->State != ECableParticleState::Fixed)
- + {
- + ParticleB->Position -= ScalarB * ErrorFactor * Delta;
- + }
- +}
- //////////////////////////////////////////////////////////////////////////
- -UCableComponent::UCableComponent( const FPostConstructInitializeProperties& PCIP )
- - : Super( PCIP )
- +UCableComponent::UCableComponent(const FPostConstructInitializeProperties& PCIP)
- + : Super(PCIP)
- {
- PrimaryComponentTick.bCanEverTick = true;
- bTickInEditor = true;
- bAutoActivate = true;
- - CableWidth = 10.f;
- - NumSegments = 10;
- - NumSides = 4;
- - EndLocation = FVector(0,0,0);
- CableLength = 100.f;
- + NumSegments = 10;
- + ParticleMass = 1.0f;
- + ConstraintStiffness = 0.5f;
- SubstepTime = 0.02f;
- SolverIterations = 1;
- +
- + CableWidth = 10.f;
- + NumSides = 4;
- TileMaterial = 1.f;
- - SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
- + SetCollisionProfileName(UCollisionProfile::BlockAll_ProfileName);
- }
- FPrimitiveSceneProxy* UCableComponent::CreateSceneProxy()
- @@ -349,137 +399,143 @@ void UCableComponent::OnRegister()
- {
- Super::OnRegister();
- - const int32 NumParticles = NumSegments+1;
- + // Save states before regenerating
- + for(int32 ParticleIdx = 0; ParticleIdx < Particles.Num(); ParticleIdx++)
- + {
- + States.Add(ParticleIdx, Particles[ParticleIdx].State);
- + }
- +
- + const int32 NumParticles = NumSegments + 1;
- + const int32 NumConstraints = NumSegments;
- Particles.Reset();
- Particles.AddUninitialized(NumParticles);
- - FVector CableStart, CableEnd;
- - GetEndPositions(CableStart, CableEnd);
- -
- - const FVector Delta = CableEnd - CableStart;
- + Constraints.Reset();
- + Constraints.AddUninitialized(NumSegments);
- - for(int32 ParticleIdx=0; ParticleIdx<NumParticles; ParticleIdx++)
- - {
- - FCableParticle& Particle = Particles[ParticleIdx];
- + FVector CableStart = GetStartPosition();
- - const float Alpha = (float)ParticleIdx/(float)NumSegments;
- - const FVector InitialPosition = CableStart + (Alpha * Delta);
- + FVector Delta = -FVector::UpVector;
- - Particle.Position = InitialPosition;
- - Particle.OldPosition = InitialPosition;
- + for(int32 ParticleIdx = 0; ParticleIdx < NumParticles; ParticleIdx++)
- + {
- + const float Alpha = (float)ParticleIdx / (float)NumParticles;
- + const FVector InitialPosition = CableStart + Delta * (Alpha * CableLength);
- - // fix particles at ends
- - if(ParticleIdx == 0 || ParticleIdx == NumParticles-1)
- + FCableParticle& Particle = Particles[ParticleIdx];
- +
- + // Resotre state
- + if(States.Contains(ParticleIdx))
- {
- - Particle.bFree = false;
- + Particle.State = States[ParticleIdx];
- }
- else
- {
- - Particle.bFree = true;
- + Particle.State = ECableParticleState::Free;
- }
- - }
- -}
- -void UCableComponent::VerletIntegrate(float InSubstepTime, const FVector& Gravity)
- -{
- - const int32 NumParticles = NumSegments+1;
- - const float SubstepTimeSqr = InSubstepTime * InSubstepTime;
- + Particle.Position = InitialPosition;
- + Particle.OldPosition = InitialPosition;
- + Particle.Acceleration = FVector(0,0,0);
- + Particle.Mass = ParticleMass;
- + Particle.InverseMass = 1 / Particle.Mass;
- + Particle.AttachedTo.OtherActor = 0;
- + }
- - for(int32 ParticleIdx=0; ParticleIdx<NumParticles; ParticleIdx++)
- + for(int32 ConstraintIdx = 0; ConstraintIdx < NumConstraints; ConstraintIdx++)
- {
- - FCableParticle& Particle = Particles[ParticleIdx];
- - if(Particle.bFree)
- - {
- - const FVector Vel = Particle.Position - Particle.OldPosition;
- - const FVector NewPosition = Particle.Position + Vel + (SubstepTimeSqr * Gravity);
- + FCableParticlesConstraint& Constraint = Constraints[ConstraintIdx];
- - Particle.OldPosition = Particle.Position;
- - Particle.Position = NewPosition;
- - }
- + Constraint.ParticleA = &Particles[ConstraintIdx];
- + Constraint.ParticleB = &Particles[ConstraintIdx + 1];
- + Constraint.RestLength = CableLength / NumConstraints;
- + Constraint.BreakLength = Constraint.RestLength * Constraint.BreakLengthFactor;
- + Constraint.Stiffness = ConstraintStiffness;
- }
- +
- + // Fix start particle
- + Particles[0].State = ECableParticleState::Fixed;
- + //Particles.Last().State = ECableParticleState::Fixed;
- }
- -/** Solve a single distance constraint between a pair of particles */
- -static void SolveDistanceConstraint(FCableParticle& ParticleA, FCableParticle& ParticleB, float DesiredDistance)
- +void UCableComponent::PerformSubstep(float InSubstepTime, const FVector& Gravity)
- {
- - // Find current vector between particles
- - FVector Delta = ParticleB.Position - ParticleA.Position;
- - //
- - float CurrentDistance = Delta.Size();
- - float ErrorFactor = (CurrentDistance - DesiredDistance)/CurrentDistance;
- + const int32 NumParticles = Particles.Num();
- + const int32 NumConstraints = Constraints.Num();
- - // Only move free particles to satisfy constraints
- - if(ParticleA.bFree && ParticleB.bFree)
- + // Perform verlet integration on every particle
- + for(int32 ParticleIdx = 0; ParticleIdx < NumParticles; ParticleIdx++)
- {
- - ParticleA.Position += ErrorFactor * 0.5f * Delta;
- - ParticleB.Position -= ErrorFactor * 0.5f * Delta;
- + Particles[ParticleIdx].VerletIntegrate(InSubstepTime, Gravity);
- }
- - else if(ParticleA.bFree)
- - {
- - ParticleA.Position += ErrorFactor * Delta;
- - }
- - else if(ParticleB.bFree)
- - {
- - ParticleB.Position -= ErrorFactor * Delta;
- - }
- -}
- -
- -void UCableComponent::SolveConstraints()
- -{
- - const float SegmentLength = CableLength/(float)NumSegments;
- - // For each iteration..
- - for(int32 IterationIdx=0; IterationIdx<SolverIterations; IterationIdx++)
- + // Solve distance constraint between particles for each iteration
- + for(int32 IterationIdx = 0; IterationIdx < SolverIterations; IterationIdx++)
- {
- - // For each segment..
- - for(int32 SegIdx=0; SegIdx<NumSegments; SegIdx++)
- + for(int32 ConstraintIdx = 0; ConstraintIdx < NumConstraints; ConstraintIdx++)
- {
- - FCableParticle& ParticleA = Particles[SegIdx];
- - FCableParticle& ParticleB = Particles[SegIdx+1];
- - // Solve for this pair of particles
- - SolveDistanceConstraint(ParticleA, ParticleB, SegmentLength);
- + Constraints[ConstraintIdx].Solve();
- }
- }
- }
- -void UCableComponent::PerformSubstep(float InSubstepTime, const FVector& Gravity)
- +FVector UCableComponent::GetStartPosition()
- {
- - VerletIntegrate(InSubstepTime, Gravity);
- - SolveConstraints();
- + // Start position is just component position
- + return GetComponentLocation();
- }
- -void UCableComponent::GetEndPositions(FVector& OutStartPosition, FVector& OutEndPosition)
- +void UCableComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
- {
- - // Start position is just component position
- - OutStartPosition = GetComponentLocation();
- + Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
- - // See if we want to attach the other end to some other component
- - USceneComponent* EndComponent = AttachEndTo.GetComponent(GetOwner());
- - if(EndComponent == NULL)
- + for(int32 ParticleIdx = 0; ParticleIdx < Particles.Num(); ParticleIdx++)
- {
- - EndComponent = this;
- - }
- + FCableParticle& Particle = Particles[ParticleIdx];
- - OutEndPosition = EndComponent->ComponentToWorld.TransformPosition(EndLocation);
- -}
- + switch(Particle.State)
- + {
- + case ECableParticleState::Free:
- + DrawDebugPoint(GetWorld(), Particle.Position, 10.0f, FColor::Green);
- + break;
- + case ECableParticleState::Fixed:
- + DrawDebugPoint(GetWorld(), Particle.Position, 10.0f, FColor::Red);
- -void UCableComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
- -{
- - Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
- + if(Particle.AttachedTo.OtherActor->IsValidLowLevel())
- + {
- + Particle.Position = Particle.AttachedTo.GetComponent(GetOwner())->GetComponentLocation();
- + }
- +
- + break;
- + case ECableParticleState::Attached:
- + DrawDebugPoint(GetWorld(), Particle.Position, 10.0f, FColor::Blue);
- +
- + if(Particle.AttachedTo.OtherActor->IsValidLowLevel())
- + {
- + Particle.AttachedTo.GetComponent(GetOwner())->SetWorldLocation(Particle.Position);
- +
- + UPrimitiveComponent* Primitive = Particle.AttachedTo.OtherActor->FindComponentByClass<UPrimitiveComponent>();
- +
- + if(Primitive)
- + {
- + Particle.Mass = ParticleMass + Primitive->GetMass();
- + Particle.InverseMass = 1 / Particle.Mass;
- + }
- + }
- +
- + break;
- + }
- + }
- const FVector Gravity = FVector(0, 0, GetWorld()->GetGravityZ());
- // Update end points
- - FVector CableStart, CableEnd;
- - GetEndPositions(CableStart, CableEnd);
- + FVector CableStart = GetStartPosition();
- FCableParticle& StartParticle = Particles[0];
- StartParticle.Position = StartParticle.OldPosition = CableStart;
- -
- - FCableParticle& EndParticle = Particles[NumSegments];
- - EndParticle.Position = EndParticle.OldPosition = CableEnd;
- -
- +
- // Ensure a non-zero substep
- float UseSubstep = FMath::Max(SubstepTime, 0.005f);
- @@ -506,9 +562,9 @@ void UCableComponent::SendRenderDynamicData_Concurrent()
- FCableDynamicData* DynamicData = new FCableDynamicData;
- // Transform current positions from particles into component-space array
- - int32 NumPoints = NumSegments+1;
- + int32 NumPoints = Particles.Num();
- DynamicData->CablePoints.AddUninitialized(NumPoints);
- - for(int32 PointIdx=0; PointIdx<NumPoints; PointIdx++)
- + for(int32 PointIdx = 0; PointIdx < NumPoints; PointIdx++)
- {
- DynamicData->CablePoints[PointIdx] = ComponentToWorld.InverseTransformPosition(Particles[PointIdx].Position);
- }
- @@ -528,7 +584,8 @@ FBoxSphereBounds UCableComponent::CalcBounds(const FTransform & LocalToWorld) co
- {
- // Calculate bounding box of cable points
- FBox CableBox(0);
- - for(int32 ParticleIdx=0; ParticleIdx<Particles.Num(); ParticleIdx++)
- +
- + for(int32 ParticleIdx = 0; ParticleIdx < Particles.Num(); ParticleIdx++)
- {
- const FCableParticle& Particle = Particles[ParticleIdx];
- CableBox += Particle.Position;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement