void UDestructibleComponent::SetChunkVisible( int32 ChunkIndex, bool bVisible ) { #if WITH_APEX // Bone 0 is a dummy root bone const int32 BoneIndex = ChunkIdxToBoneIdx(ChunkIndex); if( bVisible ) { UnHideBone(BoneIndex); if (NULL != ApexDestructibleActor) { physx::PxShape** PShapes; const physx::PxU32 PShapeCount = ApexDestructibleActor->getChunkPhysXShapes(PShapes, ChunkIndex); if (PShapeCount > 0) { const physx::PxMat44 ChunkPoseRT = ApexDestructibleActor->getChunkPose(ChunkIndex); // Unscaled const physx::PxTransform Transform(ChunkPoseRT); SetChunkWorldRT(ChunkIndex, P2UQuat(Transform.q), P2UVector(Transform.p)); } } } else { HideBone(BoneIndex, PBO_None); } // Mark the transform as dirty, so the bounds are updated and sent to the render thread MarkRenderTransformDirty(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); #endif }
void UNiagaraComponent::TickComponent(float DeltaSeconds, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { // EmitterAge += DeltaSeconds; if (EffectInstance) { static FNiagaraVariableInfo Const_Zero(TEXT("ZERO"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_DeltaTime(TEXT("Delta Time"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_EmitterPos(TEXT("Emitter Position"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_EmitterAge(TEXT("Emitter Age"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_EmitterX(TEXT("Emitter X Axis"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_EmitterY(TEXT("Emitter Y Axis"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_EmitterZ(TEXT("Emitter Z Axis"), ENiagaraDataType::Vector); static FNiagaraVariableInfo Const_EmitterTransform(TEXT("Emitter Transform"), ENiagaraDataType::Matrix); //Todo, open this up to the UI and setting via code and BPs. EffectInstance->SetConstant(Const_Zero, FVector4(0.0f, 0.0f, 0.0f, 0.0f)); // zero constant EffectInstance->SetConstant(Const_DeltaTime, FVector4(DeltaSeconds, DeltaSeconds, DeltaSeconds, DeltaSeconds)); EffectInstance->SetConstant(Const_EmitterPos, FVector4(ComponentToWorld.GetTranslation())); EffectInstance->SetConstant(Const_EmitterX, FVector4(ComponentToWorld.GetUnitAxis(EAxis::X))); EffectInstance->SetConstant(Const_EmitterY, FVector4(ComponentToWorld.GetUnitAxis(EAxis::Y))); EffectInstance->SetConstant(Const_EmitterZ, FVector4(ComponentToWorld.GetUnitAxis(EAxis::Z))); EffectInstance->SetConstant(Const_EmitterTransform, ComponentToWorld.ToMatrixWithScale()); EffectInstance->Tick(DeltaSeconds); } UpdateComponentToWorld(); MarkRenderDynamicDataDirty(); }
void UPoseableMeshComponent::SetBoneTransformByName(FName BoneName, const FTransform& InTransform, EBoneSpaces::Type BoneSpace) { if( !SkeletalMesh || !RequiredBones.IsValid() ) { return; } int32 BoneIndex = GetBoneIndex(BoneName); if(BoneIndex >=0 && BoneIndex < LocalAtoms.Num()) { LocalAtoms[BoneIndex] = InTransform; // If we haven't requested local space we need to transform the position passed in //if(BoneSpace != EBoneSpaces::LocalSpace) { if(BoneSpace == EBoneSpaces::WorldSpace) { LocalAtoms[BoneIndex].SetToRelativeTransform(GetComponentToWorld()); } int32 ParentIndex = RequiredBones.GetParentBoneIndex(BoneIndex); if(ParentIndex >=0) { FA2CSPose CSPose; CSPose.AllocateLocalPoses(RequiredBones, LocalAtoms); LocalAtoms[BoneIndex].SetToRelativeTransform(CSPose.GetComponentSpaceTransform(ParentIndex)); } // Need to send new state to render thread MarkRenderDynamicDataDirty(); } } }
void USkeletalMeshComponent::PostBlendPhysics() { SCOPE_CYCLE_COUNTER(STAT_UpdateLocalToWorldAndOverlaps); // Flip bone buffer and send 'post anim' notification FinalizeBoneTransform(); // Update Child Transform - The above function changes bone transform, so will need to update child transform UpdateChildTransforms(); // animation often change overlap. UpdateOverlaps(); // Cached local bounds are now out of date InvalidateCachedBounds(); // update bounds UpdateBounds(); // Need to send new bounds to MarkRenderTransformDirty(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); }
void UDestructibleComponent::SetChunksWorldTM(const TArray<FUpdateChunksInfo>& UpdateInfos) { const FQuat InvRotation = ComponentToWorld.GetRotation().Inverse(); for (const FUpdateChunksInfo& UpdateInfo : UpdateInfos) { // Bone 0 is a dummy root bone const int32 BoneIndex = ChunkIdxToBoneIdx(UpdateInfo.ChunkIndex); const FVector WorldTranslation = UpdateInfo.WorldTM.GetLocation(); const FQuat WorldRotation = UpdateInfo.WorldTM.GetRotation(); const FQuat BoneRotation = InvRotation*WorldRotation; const FVector BoneTranslation = InvRotation.RotateVector(WorldTranslation - ComponentToWorld.GetTranslation()) / ComponentToWorld.GetScale3D(); GetEditableSpaceBases()[BoneIndex] = FTransform(BoneRotation, BoneTranslation); } // Mark the transform as dirty, so the bounds are updated and sent to the render thread MarkRenderTransformDirty(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); //Update bone visibilty and flip the editable space base buffer FlipEditableSpaceBases(); }
void UDestructibleComponent::RefreshBoneTransforms() { #if WITH_APEX if(ApexDestructibleActor != NULL && SkeletalMesh) { UDestructibleMesh* TheDestructibleMesh = GetDestructibleMesh(); // Save a pointer to the APEX NxDestructibleAsset physx::NxDestructibleAsset* ApexDestructibleAsset = TheDestructibleMesh->ApexDestructibleAsset; check(ApexDestructibleAsset); { // Lock here so we don't encounter race conditions with the destruction processing FPhysScene* PhysScene = World->GetPhysicsScene(); check(PhysScene); const uint32 SceneType = (BodyInstance.bUseAsyncScene && PhysScene->HasAsyncScene()) ? PST_Async : PST_Sync; PxScene* PScene = PhysScene->GetPhysXScene(SceneType); check(PScene); SCOPED_SCENE_WRITE_LOCK(PScene); SCOPED_SCENE_READ_LOCK(PScene); // Try to acquire event buffer const physx::NxDestructibleChunkEvent* EventBuffer; physx::PxU32 EventBufferSize; if (ApexDestructibleActor->acquireChunkEventBuffer(EventBuffer, EventBufferSize)) { // Buffer acquired while (EventBufferSize--) { const physx::NxDestructibleChunkEvent& Event = *EventBuffer++; // Right now the only events are visibility changes. So as an optimization we won't check for the event type. // if (Event.event & physx::NxDestructibleChunkEvent::VisibilityChanged) const bool bVisible = (Event.event & physx::NxDestructibleChunkEvent::ChunkVisible) != 0; SetChunkVisible(Event.chunkIndex, bVisible); } // Release buffer (will be cleared) ApexDestructibleActor->releaseChunkEventBuffer(); } } // Update poses for visible chunks const physx::PxU16* VisibleChunks = ApexDestructibleActor->getVisibleChunks(); physx::PxU16 VisibleChunkCount = ApexDestructibleActor->getNumVisibleChunks(); while (VisibleChunkCount--) { const physx::PxU16 ChunkIndex = *VisibleChunks++; // BRGTODO : Make a direct method to access the Px objects' quats const physx::PxMat44 ChunkPoseRT = ApexDestructibleActor->getChunkPose(ChunkIndex); // Unscaled const physx::PxTransform Transform(ChunkPoseRT); SetChunkWorldRT(ChunkIndex, P2UQuat(Transform.q), P2UVector(Transform.p)); } // Send bones to render thread at end of frame MarkRenderDynamicDataDirty(); } #endif // #if WITH_APEX }
void UPaperFlipbookComponent::CalculateCurrentFrame() { const int32 LastCachedFrame = CachedFrameIndex; CachedFrameIndex = (SourceFlipbook != nullptr) ? SourceFlipbook->GetKeyFrameIndexAtTime(AccumulatedTime) : INDEX_NONE; if (CachedFrameIndex != LastCachedFrame) { // Update children transforms in case we have anything attached to an animated socket UpdateChildTransforms(); if ((SourceFlipbook != nullptr) && (SourceFlipbook->GetCollisionSource() == EFlipbookCollisionMode::EachFrameCollision)) { FlipbookChangedPhysicsState(); } // Indicate we need to send new dynamic data. MarkRenderDynamicDataDirty(); } }
void USkeletalMeshComponent::CompleteParallelBlendPhysics() { Exchange(AnimEvaluationContext.LocalAtoms, AnimEvaluationContext.bDoInterpolation ? CachedLocalAtoms : LocalAtoms); FlipEditableSpaceBases(); // Update Child Transform - The above function changes bone transform, so will need to update child transform UpdateChildTransforms(); // animation often change overlap. UpdateOverlaps(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); FinalizeBoneTransform(); ParallelAnimationEvaluationTask.SafeRelease(); ParallelBlendPhysicsCompletionTask.SafeRelease(); }
void UPoseableMeshComponent::RefreshBoneTransforms(FActorComponentTickFunction* TickFunction) { SCOPE_CYCLE_COUNTER(STAT_RefreshBoneTransforms); // Can't do anything without a SkeletalMesh if( !SkeletalMesh ) { return; } // Do nothing more if no bones in skeleton. if( GetNumSpaceBases() == 0 ) { return; } // We need the mesh space bone transforms now for renderer to get delta from ref pose: FillSpaceBases(); FlipEditableSpaceBases(); MarkRenderDynamicDataDirty(); }
void UDestructibleComponent::SetChunkWorldRT( int32 ChunkIndex, const FQuat& WorldRotation, const FVector& WorldTranslation ) { // Bone 0 is a dummy root bone const int32 BoneIndex = ChunkIdxToBoneIdx(ChunkIndex); // Mark the transform as dirty, so the bounds are updated and sent to the render thread MarkRenderTransformDirty(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); #if 0 // Scale is already applied to the ComponentToWorld transform, and is carried into the bones _locally_. // So there is no need to set scale in the bone local transforms const FTransform WorldRT(WorldRotation, WorldTranslation, ComponentToWorld.GetScale3D()); SpaceBases(BoneIndex) = WorldRT*ComponentToWorld.Inverse(); #elif 1 // More optimal form of the above const FQuat BoneRotation = ComponentToWorld.GetRotation().Inverse()*WorldRotation; const FVector BoneTranslation = ComponentToWorld.GetRotation().Inverse().RotateVector(WorldTranslation - ComponentToWorld.GetTranslation())/ComponentToWorld.GetScale3D(); GetEditableSpaceBases()[BoneIndex] = FTransform(BoneRotation, BoneTranslation); #endif }
/** Tick */ void UFluidSurfaceComponent::TickComponent( float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction ) { Super::TickComponent( DeltaTime, TickType, ThisTickFunction ); LastDeltaTime = DeltaTime; float SimStep = 1.f / UpdateRate; static float Time = 0.0f; #if WITH_EDITOR /* Only update if checked */ if( !UpdateComponent ) return; #endif /* If this water hasn't been rendered for a while, stop updating */ if (LastRenderTime > 0 && GetWorld()->TimeSeconds - LastRenderTime > 1) return; Time += DeltaTime; if( Time > SimStep ) { Time = 0.0f; LatestVerts = !LatestVerts; /* Add ripples for actors in the water */ TArray<struct FOverlapResult> OverlappingActors; FCollisionShape CollisionShape; CollisionShape.SetBox( FluidBoundingBox.GetExtent( ) ); /* Find overlapping actors */ GetWorld()->OverlapMultiByChannel(OverlappingActors, GetComponentLocation(), GetComponentQuat(), ECC_WorldDynamic, CollisionShape, FCollisionQueryParams(false)); // @todo: handle better /* Iterate through found overlapping actors */ for( int i = 0; i < OverlappingActors.Num( ); i++ ) { TWeakObjectPtr<AActor> Actor = OverlappingActors[ i ].Actor; /* Dont care about self and modifiers */ if( Actor != NULL && !Actor->IsA( AFluidSurfaceActor::StaticClass( ) ) && !Actor->IsA( AFluidSurfaceModifier::StaticClass( ) ) ) { FVector LocalVel = GetWorldToComponent( ).TransformVector( Actor->GetVelocity( ) ); float HorizVelMag = LocalVel.Size( ); Pling( Actor->GetActorLocation( ), RippleVelocityFactor * HorizVelMag, Actor->GetSimpleCollisionRadius( ) ); } } /* Do test ripple (moving around in a circle) */ if( GIsEditor && TestRipple ) { TestRippleAng += SimStep * MyU2Rad * TestRippleSpeed; FVector WorldRipplePos, LocalRipplePos; float RippleRadius = 0.3f * ( FluidXSize - 1 ) * FluidGridSpacing; if( FluidGridType == EFluidGridType::FGT_Hexagonal ) RippleRadius = FMath::Max( RippleRadius, 0.3f * ( FluidYSize - 1 ) * FluidGridSpacing * ROOT3OVER2 ); else RippleRadius = FMath::Max( RippleRadius, 0.3f * ( FluidYSize - 1 ) * FluidGridSpacing ); LocalRipplePos.X = ( RippleRadius * FMath::Sin( TestRippleAng ) ); LocalRipplePos.Y = ( RippleRadius * FMath::Cos( TestRippleAng ) ); LocalRipplePos.Z = 0.f; WorldRipplePos = ComponentToWorld.TransformPosition( LocalRipplePos ); Pling( WorldRipplePos, TestRippleStrength, TestRippleRadius ); } /* Add modifier effects */ for( int i = 0; i < Modifiers.Num( ); i++ ) { if( Modifiers[ i ] && Modifiers[ i ]->Active ) Modifiers[ i ]->Update( DeltaTime ); } /* Need to send new dynamic data */ MarkRenderDynamicDataDirty( ); } }
void UPaperTileMapComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { // Indicate we need to send new dynamic data. MarkRenderDynamicDataDirty(); }
void USkeletalMeshComponent::RefreshBoneTransforms() { SCOPE_CYCLE_COUNTER(STAT_RefreshBoneTransforms); // Can't do anything without a SkeletalMesh // Do nothing more if no bones in skeleton. if( !SkeletalMesh || SpaceBases.Num() == 0 ) { return; } AActor * Owner = GetOwner(); const FAnimUpdateRateParameters & UpdateRateParams = Owner ? Owner->AnimUpdateRateParams : FAnimUpdateRateParameters(); { FScopeLockPhysXWriter LockPhysXForWriting; // Recalculate the RequiredBones array, if necessary if( !bRequiredBonesUpToDate ) { RecalcRequiredBones(PredictedLODLevel); } // Update rate turned off, evaluate every frame. if( !bEnableUpdateRateOptimizations || (UpdateRateParams.GetEvaluationRate() <= 1) ) { // evaluate pure animations, and fill up LocalAtoms EvaluateAnimation(); // We need the mesh space bone transforms now for renderer to get delta from ref pose: FillSpaceBases(); // Invalidate cached bones. CachedLocalAtoms.Empty(); CachedSpaceBases.Empty(); } else { // figure out if our cache is invalid. const bool bInvalidCachedBones = (LocalAtoms.Num() != SkeletalMesh->RefSkeleton.GetNum()) || (LocalAtoms.Num() != CachedLocalAtoms.Num()) || (SpaceBases.Num() != CachedSpaceBases.Num()); // If cache is invalid, we need to rebuild it. And we can't interpolate. // (same path if we're not interpolating and not skipping a frame). if( bInvalidCachedBones || (!UpdateRateParams.ShouldInterpolateSkippedFrames() && !UpdateRateParams.ShouldSkipEvaluation()) ) { // evaluate pure animations, and fill up LocalAtoms EvaluateAnimation(); // Fill SpaceBases from LocalAtoms FillSpaceBases(); // Cache bones CachedLocalAtoms = LocalAtoms; CachedSpaceBases = SpaceBases; } else { // No interpolation, just copy // @todo: if we don't blend any physics, we could even skip the copy. if( !UpdateRateParams.ShouldInterpolateSkippedFrames() ) { LocalAtoms = CachedLocalAtoms; SpaceBases = CachedSpaceBases; } else { // If we are not skipping evaluation this frame, refresh cache. if( !UpdateRateParams.ShouldSkipEvaluation() ) { // Preserve LocalAtoms and SpaceBases, so we can keep interpolation. Exchange(LocalAtoms, CachedLocalAtoms); Exchange(SpaceBases, CachedSpaceBases); // evaluate pure animations, and fill up LocalAtoms EvaluateAnimation(); // Fill SpaceBases from LocalAtoms FillSpaceBases(); Exchange(LocalAtoms, CachedLocalAtoms); Exchange(SpaceBases, CachedSpaceBases); } // Interpolate { SCOPE_CYCLE_COUNTER(STAT_InterpolateSkippedFrames); const float Alpha = 0.25f + (1.f / float(FMath::Max(UpdateRateParams.GetEvaluationRate(), 2) * 2)); FAnimationRuntime::LerpBoneTransforms(LocalAtoms, CachedLocalAtoms, Alpha, RequiredBones); FAnimationRuntime::LerpBoneTransforms(SpaceBases, CachedSpaceBases, Alpha, RequiredBones); } } } } // Transforms updated, cached local bounds are now out of date. InvalidateCachedBounds(); // update physics data from animated data UpdateKinematicBonesToPhysics(false); UpdateRBJointMotors(); } // @todo anim : hack TTP 224385 ANIM: Skeletalmesh double buffer // this is problem because intermediate buffer changes physics position as well // this causes issue where a half of frame, physics position is fixed with anim pose, and the other half is real simulated position // if you enable physics in tick, since that's before physics update, you'll get animation pose dominating physics pose, which isn't what you want. (Or what you'll see) // so do not update transform if physics is on. This problem will be solved by double buffer, when we keep one buffer for intermediate, and the other buffer for result query { FScopeLockPhysXReader LockPhysXForReading; if( !IsSimulatingPhysics() ) { SCOPE_CYCLE_COUNTER(STAT_UpdateLocalToWorldAndOverlaps); // New bone positions need to be sent to render thread UpdateComponentToWorld(); // animation often change overlap. UpdateOverlaps(); } } MarkRenderDynamicDataDirty(); }
void UDestructibleComponent::SetChunkVisible( int32 ChunkIndex, bool bVisible ) { // Bone 0 is a dummy root bone const int32 BoneIndex = ChunkIdxToBoneIdx(ChunkIndex); bool bClearActorFromChunkInfo = false; if( bVisible ) { UnHideBone(BoneIndex); #if WITH_APEX PxRigidDynamic* PActor = ApexDestructibleActor != NULL ? ApexDestructibleActor->getChunkPhysXActor(ChunkIndex) : NULL; UDestructibleMesh* DMesh = GetDestructibleMesh(); if (PActor != NULL) { // If actor has already a chunk info and userdata, we just make sure it is valid and update the // physx actor if needed. We do NOT do this for FormExtended structures, as in this case, the shapes/actors // are moved to the 1st structure object internally by APEX. if(PActor->userData != NULL && !DMesh->DefaultDestructibleParameters.Flags.bFormExtendedStructures) { FDestructibleChunkInfo* CI = FPhysxUserData::Get<FDestructibleChunkInfo>(PActor->userData); checkf(CI, TEXT("If a chunk actor has user data and it is not a DestructibleChunkInfo, something is messed up.")); //check(CI->OwningComponent == this); if (CI->ChunkIndex != ChunkIndex) { // grab the old actor and clear its user data, as we steal the ChunkInfo here if (CI->Actor && CI->Actor != PActor) { CI->Actor->userData = NULL; } CI->ChunkIndex = ChunkIndex; } CI->OwningComponent = this; CI->Actor = PActor; } else if (PActor->userData == NULL) { // Setup the user data to have a proper chunk - actor mapping int32 InfoIndex = ChunkInfos.AddUninitialized(); FDestructibleChunkInfo* CI = &ChunkInfos[InfoIndex]; CI->Index = InfoIndex; CI->ChunkIndex = ChunkIndex; CI->OwningComponent = this; CI->Actor = PActor; int32 UserDataIdx = PhysxChunkUserData.Add(FPhysxUserData(CI)); check(InfoIndex == UserDataIdx); PActor->userData = &PhysxChunkUserData[UserDataIdx]; // Set collision response to non-root chunks if (GetDestructibleMesh()->ApexDestructibleAsset->getChunkParentIndex(ChunkIndex) >= 0) { SetCollisionResponseForActor(ChunkCollisionResponse, PActor, ChunkIndex); } } } else { bClearActorFromChunkInfo = true; } #endif // WITH_APEX } else { HideBone(BoneIndex, PBO_None); bClearActorFromChunkInfo = true; } #if WITH_APEX if (bClearActorFromChunkInfo) { // Make sure we clear the physx actor pointer of the chunk info as it might (and probably will) be // invalid from now on for (int32 i=0; i < ChunkInfos.Num(); ++i) { if (ChunkInfos[i].ChunkIndex == ChunkIndex) { ChunkInfos[i].Actor = NULL; break; } } } #endif // WITH_APEX // Mark the transform as dirty, so the bounds are updated and sent to the render thread MarkRenderTransformDirty(); // New bone positions need to be sent to render thread MarkRenderDynamicDataDirty(); }