bool FHierarchicalLODUtilities::BuildStaticMeshForLODActor(ALODActor* LODActor, UPackage* AssetsOuter, const FHierarchicalSimplification& LODSetup, const uint32 LODIndex) { if (AssetsOuter && LODActor) { if (!LODActor->IsDirty()) { return false; } const FScopedTransaction Transaction(LOCTEXT("UndoAction_BuildProxyMesh", "Building Proxy Mesh for Cluster")); LODActor->Modify(); // Delete actor assets before generating new ones FHierarchicalLODUtilities::DestroyClusterData(LODActor); TArray<UStaticMeshComponent*> AllComponents; { FScopedSlowTask SlowTask(LODActor->SubActors.Num(), (LOCTEXT("HierarchicalLODUtils_CollectStaticMeshes", "Collecting Static Meshes for Cluster"))); SlowTask.MakeDialog(); for (auto& Actor : LODActor->SubActors) { TArray<UStaticMeshComponent*> Components; if (Actor->IsA<ALODActor>()) { ExtractStaticMeshComponentsFromLODActor(Actor, Components); } else { Actor->GetComponents<UStaticMeshComponent>(Components); } // TODO: support instanced static meshes Components.RemoveAll([](UStaticMeshComponent* Val){ return Val->IsA(UInstancedStaticMeshComponent::StaticClass()); }); AllComponents.Append(Components); SlowTask.EnterProgressFrame(1); } } // it shouldn't even have come here if it didn't have any staticmesh if (ensure(AllComponents.Num() > 0)) { FScopedSlowTask SlowTask(LODActor->SubActors.Num(), (LOCTEXT("HierarchicalLODUtils_MergingMeshes", "Merging Static Meshes and creating LODActor"))); SlowTask.MakeDialog(); // In case we don't have outer generated assets should have same path as LOD level const FString AssetsPath = AssetsOuter->GetName() + TEXT("/"); AActor* FirstActor = LODActor->SubActors[0]; TArray<UObject*> OutAssets; FVector OutProxyLocation = FVector::ZeroVector; UStaticMesh* MainMesh = nullptr; // Generate proxy mesh and proxy material assets IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities"); // should give unique name, so use level + actor name const FString PackageName = FString::Printf(TEXT("LOD_%s"), *FirstActor->GetName()); if (MeshUtilities.GetMeshMergingInterface() && LODSetup.bSimplifyMesh) { TArray<AActor*> Actors; ExtractSubActorsFromLODActor(LODActor, Actors); FHierarchicalLODUtilities& Module = FModuleManager::LoadModuleChecked<FHierarchicalLODUtilities>("HierarchicalLODUtilities"); FHierarchicalLODProxyProcessor* Processor = Module.GetProxyProcessor(); FHierarchicalSimplification OverrideLODSetup = LODSetup; FMeshProxySettings ProxySettings = LODSetup.ProxySetting; if (LODActor->bOverrideMaterialMergeSettings) { ProxySettings.MaterialSettings = LODActor->MaterialSettings; } if (LODActor->bOverrideScreenSize) { ProxySettings.ScreenSize = LODActor->ScreenSize; } if (LODActor->bOverrideTransitionScreenSize) { OverrideLODSetup.TransitionScreenSize = LODActor->TransitionScreenSize; } FGuid JobID = Processor->AddProxyJob(LODActor, OverrideLODSetup); MeshUtilities.CreateProxyMesh(Actors, ProxySettings, AssetsOuter, PackageName, JobID, Processor->GetCallbackDelegate(), true, OverrideLODSetup.TransitionScreenSize); return true; } else { FMeshMergingSettings MergeSettings = LODSetup.MergeSetting; if (LODActor->bOverrideMaterialMergeSettings) { MergeSettings.MaterialSettings = LODActor->MaterialSettings; } MeshUtilities.MergeStaticMeshComponents(AllComponents, FirstActor->GetWorld(), MergeSettings, AssetsOuter, PackageName, LODIndex, OutAssets, OutProxyLocation, LODSetup.TransitionScreenSize, true); // set staticmesh for (auto& Asset : OutAssets) { UStaticMesh* StaticMesh = Cast<UStaticMesh>(Asset); if (StaticMesh) { MainMesh = StaticMesh; } } LODActor->SetStaticMesh(MainMesh); LODActor->SetActorLocation(OutProxyLocation); LODActor->SubObjects = OutAssets; // At the moment this assumes a fixed field of view of 90 degrees (horizontal and vertical axi) static const float FOVRad = 90.0f * (float)PI / 360.0f; static const FMatrix ProjectionMatrix = FPerspectiveMatrix(FOVRad, 1920, 1080, 0.01f); FBoxSphereBounds Bounds = LODActor->GetStaticMeshComponent()->CalcBounds(FTransform()); LODActor->LODDrawDistance = FHierarchicalLODUtilities::CalculateDrawDistanceFromScreenSize(Bounds.SphereRadius, LODSetup.TransitionScreenSize, ProjectionMatrix); LODActor->UpdateSubActorLODParents(); // Freshly build so mark not dirty LODActor->SetIsDirty(false); return true; } } } return false; }
FTransform UpdateBoneAtom(int32 BoneIndex, const FTransform& Atom, const T& Component) { // only the custom instantiations below are valid check(0); return FTransform(); }
void UAnimCompress_RemoveLinearKeys::ProcessAnimationTracks( UAnimSequence* AnimSeq, const TArray<FBoneData>& BoneData, TArray<FTranslationTrack>& PositionTracks, TArray<FRotationTrack>& RotationTracks, TArray<FScaleTrack>& ScaleTracks) { // extract all the data we'll need about the skeleton and animation sequence const int32 NumBones = BoneData.Num(); const int32 NumFrames = AnimSeq->NumFrames; const float SequenceLength = AnimSeq->SequenceLength; const int32 LastFrame = NumFrames-1; const float FrameRate = (float)(LastFrame) / SequenceLength; const float TimePerFrame = SequenceLength / (float)(LastFrame); const TArray<FTransform>& RefPose = AnimSeq->GetSkeleton()->GetRefLocalPoses(); const bool bHasScale = (ScaleTracks.Num() > 0); // make sure the parent key scale is properly bound to 1.0 or more ParentKeyScale = FMath::Max(ParentKeyScale, 1.0f); // generate the raw and compressed skeleton in world-space TArray<FTransform> RawWorldBones; TArray<FTransform> NewWorldBones; RawWorldBones.Empty(NumBones * NumFrames); NewWorldBones.Empty(NumBones * NumFrames); RawWorldBones.AddZeroed(NumBones * NumFrames); NewWorldBones.AddZeroed(NumBones * NumFrames); // generate an array to hold the indices of our end effectors TArray<int32> EndEffectors; EndEffectors.Empty(NumBones); // Create an array of FTransform to use as a workspace TArray<FTransform> BoneAtoms; // setup the raw bone transformation and find all end effectors for ( int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex ) { // get the raw world-atoms for this bone UpdateWorldBoneTransformTable( AnimSeq, BoneData, RefPose, BoneIndex, true, RawWorldBones); // also record all end-effectors we find const FBoneData& Bone = BoneData[BoneIndex]; if (Bone.IsEndEffector()) { EndEffectors.Add(BoneIndex); } } TArray<int32> TargetBoneIndices; // for each bone... for ( int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex ) { const FBoneData& Bone = BoneData[BoneIndex]; const int32 ParentBoneIndex = Bone.GetParent(); const int32 TrackIndex = AnimSeq->GetSkeleton()->GetAnimationTrackIndex(BoneIndex, AnimSeq, true); if (TrackIndex != INDEX_NONE) { // get the tracks we will be editing for this bone FRotationTrack& RotTrack = RotationTracks[TrackIndex]; FTranslationTrack& TransTrack = PositionTracks[TrackIndex]; const int32 NumRotKeys = RotTrack.RotKeys.Num(); const int32 NumPosKeys = TransTrack.PosKeys.Num(); const int32 NumScaleKeys = (bHasScale)? ScaleTracks[TrackIndex].ScaleKeys.Num() : 0; check( (NumPosKeys == 1) || (NumRotKeys == 1) || (NumPosKeys == NumRotKeys) ); // build an array of end effectors we need to monitor TargetBoneIndices.Reset(NumBones); int32 HighestTargetBoneIndex = BoneIndex; int32 FurthestTargetBoneIndex = BoneIndex; int32 ShortestChain = 0; float OffsetLength= -1.0f; for (int32 EffectorIndex=0; EffectorIndex < EndEffectors.Num(); ++EffectorIndex) { const int32 EffectorBoneIndex = EndEffectors[EffectorIndex]; const FBoneData& EffectorBoneData = BoneData[EffectorBoneIndex]; int32 RootIndex = EffectorBoneData.BonesToRoot.Find(BoneIndex); if (RootIndex != INDEX_NONE) { if (ShortestChain == 0 || (RootIndex+1) < ShortestChain) { ShortestChain = (RootIndex+1); } TargetBoneIndices.Add(EffectorBoneIndex); HighestTargetBoneIndex = FMath::Max(HighestTargetBoneIndex, EffectorBoneIndex); float ChainLength= 0.0f; for (long FamilyIndex=0; FamilyIndex < RootIndex; ++FamilyIndex) { const int32 NextParentBoneIndex= EffectorBoneData.BonesToRoot[FamilyIndex]; ChainLength += RefPose[NextParentBoneIndex].GetTranslation().Size(); } if (ChainLength > OffsetLength) { FurthestTargetBoneIndex = EffectorBoneIndex; OffsetLength = ChainLength; } } } // if requested, retarget the FBoneAtoms towards the target end effectors if (bRetarget) { if (NumScaleKeys > 0 && ParentBoneIndex != INDEX_NONE) { // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); FScaleTrack& ScaleTrack = ScaleTracks[TrackIndex]; // adjust all translation keys to align better with the destination for ( int32 KeyIndex = 0; KeyIndex < NumScaleKeys; ++KeyIndex ) { FVector& Key= ScaleTrack.ScaleKeys[KeyIndex]; const int32 FrameIndex= FMath::Clamp(KeyIndex, 0, LastFrame); const FTransform& NewWorldParent = NewWorldBones[(ParentBoneIndex*NumFrames) + FrameIndex]; const FTransform& RawWorldChild = RawWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& RelTM = (RawWorldChild.GetRelativeTransform(NewWorldParent)); const FTransform Delta = FTransform(RelTM); Key = Delta.GetScale3D(); } } if (NumRotKeys > 0 && ParentBoneIndex != INDEX_NONE) { if (HighestTargetBoneIndex == BoneIndex) { for ( int32 KeyIndex = 0; KeyIndex < NumRotKeys; ++KeyIndex ) { FQuat& Key = RotTrack.RotKeys[KeyIndex]; check(ParentBoneIndex != INDEX_NONE); const int32 FrameIndex = FMath::Clamp(KeyIndex, 0, LastFrame); FTransform NewWorldParent = NewWorldBones[(ParentBoneIndex*NumFrames) + FrameIndex]; FTransform RawWorldChild = RawWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& RelTM = (RawWorldChild.GetRelativeTransform(NewWorldParent)); FQuat Rot = FTransform(RelTM).GetRotation(); const FQuat& AlignedKey = EnforceShortestArc(Key, Rot); Key = AlignedKey; } } else { // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // adjust all rotation keys towards the end effector target for ( int32 KeyIndex = 0; KeyIndex < NumRotKeys; ++KeyIndex ) { FQuat& Key = RotTrack.RotKeys[KeyIndex]; const int32 FrameIndex = FMath::Clamp(KeyIndex, 0, LastFrame); const FTransform& NewWorldTransform = NewWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& DesiredChildTransform = RawWorldBones[(FurthestTargetBoneIndex*NumFrames) + FrameIndex].GetRelativeTransform(NewWorldTransform); const FTransform& CurrentChildTransform = NewWorldBones[(FurthestTargetBoneIndex*NumFrames) + FrameIndex].GetRelativeTransform(NewWorldTransform); // find the two vectors which represent the angular error we are trying to correct const FVector& CurrentHeading = CurrentChildTransform.GetTranslation(); const FVector& DesiredHeading = DesiredChildTransform.GetTranslation(); // if these are valid, we can continue if (!CurrentHeading.IsNearlyZero() && !DesiredHeading.IsNearlyZero()) { const float DotResult = CurrentHeading.GetSafeNormal() | DesiredHeading.GetSafeNormal(); // limit the range we will retarget to something reasonable (~60 degrees) if (DotResult < 1.0f && DotResult > 0.5f) { FQuat Adjustment= FQuat::FindBetweenVectors(CurrentHeading, DesiredHeading); Adjustment= EnforceShortestArc(FQuat::Identity, Adjustment); const FVector Test = Adjustment.RotateVector(CurrentHeading); const float DeltaSqr = (Test - DesiredHeading).SizeSquared(); if (DeltaSqr < FMath::Square(0.001f)) { FQuat NewKey = Adjustment * Key; NewKey.Normalize(); const FQuat& AlignedKey = EnforceShortestArc(Key, NewKey); Key = AlignedKey; } } } } } } if (NumPosKeys > 0 && ParentBoneIndex != INDEX_NONE) { // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // adjust all translation keys to align better with the destination for ( int32 KeyIndex = 0; KeyIndex < NumPosKeys; ++KeyIndex ) { FVector& Key= TransTrack.PosKeys[KeyIndex]; const int32 FrameIndex= FMath::Clamp(KeyIndex, 0, LastFrame); FTransform NewWorldParent = NewWorldBones[(ParentBoneIndex*NumFrames) + FrameIndex]; FTransform RawWorldChild = RawWorldBones[(BoneIndex*NumFrames) + FrameIndex]; const FTransform& RelTM = RawWorldChild.GetRelativeTransform(NewWorldParent); const FTransform Delta = FTransform(RelTM); ensure (!Delta.ContainsNaN()); Key = Delta.GetTranslation(); } } } // look for a parent track to reference as a guide int32 GuideTrackIndex = INDEX_NONE; if (ParentKeyScale > 1.0f) { for (long FamilyIndex=0; (FamilyIndex < Bone.BonesToRoot.Num()) && (GuideTrackIndex == INDEX_NONE); ++FamilyIndex) { const int32 NextParentBoneIndex= Bone.BonesToRoot[FamilyIndex]; GuideTrackIndex = AnimSeq->GetSkeleton()->GetAnimationTrackIndex(NextParentBoneIndex, AnimSeq, true); } } // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // rebuild the BoneAtoms table using the current set of keys UpdateBoneAtomList(AnimSeq, BoneIndex, TrackIndex, NumFrames, TimePerFrame, BoneAtoms); // determine the EndEffectorTolerance. // We use the Maximum value by default, and the Minimum value // as we approach the end effectors float EndEffectorTolerance = MaxEffectorDiff; if (ShortestChain <= 1) { EndEffectorTolerance = MinEffectorDiff; } // Determine if a guidance track should be used to aid in choosing keys to retain TArray<float>* GuidanceTrack = NULL; float GuidanceScale = 1.0f; if (GuideTrackIndex != INDEX_NONE) { FTranslationTrack& GuideTransTrack = PositionTracks[GuideTrackIndex]; GuidanceTrack = &GuideTransTrack.Times; GuidanceScale = ParentKeyScale; } // if the TargetBoneIndices array is empty, then this bone is an end effector. // so we add it to the list to maintain our tolerance checks if (TargetBoneIndices.Num() == 0) { TargetBoneIndices.Add(BoneIndex); } if (bActuallyFilterLinearKeys) { if (bHasScale) { FScaleTrack& ScaleTrack = ScaleTracks[TrackIndex]; // filter out translations we can approximate through interpolation FilterLinearKeysTemplate<FVector>( ScaleTrack.ScaleKeys, ScaleTrack.Times, BoneAtoms, GuidanceTrack, RawWorldBones, NewWorldBones, TargetBoneIndices, NumFrames, BoneIndex, ParentBoneIndex, GuidanceScale, MaxScaleDiff, EndEffectorTolerance, EffectorDiffSocket, BoneData); // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // rebuild the BoneAtoms table using the current set of keys UpdateBoneAtomList(AnimSeq, BoneIndex, TrackIndex, NumFrames, TimePerFrame, BoneAtoms); } // filter out translations we can approximate through interpolation FilterLinearKeysTemplate<FVector>( TransTrack.PosKeys, TransTrack.Times, BoneAtoms, GuidanceTrack, RawWorldBones, NewWorldBones, TargetBoneIndices, NumFrames, BoneIndex, ParentBoneIndex, GuidanceScale, MaxPosDiff, EndEffectorTolerance, EffectorDiffSocket, BoneData); // update our bone table from the current bone through the last end effector we need to test UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, HighestTargetBoneIndex, false, NewWorldBones); // rebuild the BoneAtoms table using the current set of keys UpdateBoneAtomList(AnimSeq, BoneIndex, TrackIndex, NumFrames, TimePerFrame, BoneAtoms); // filter out rotations we can approximate through interpolation FilterLinearKeysTemplate<FQuat>( RotTrack.RotKeys, RotTrack.Times, BoneAtoms, GuidanceTrack, RawWorldBones, NewWorldBones, TargetBoneIndices, NumFrames, BoneIndex, ParentBoneIndex, GuidanceScale, MaxAngleDiff, EndEffectorTolerance, EffectorDiffSocket, BoneData); } } // make sure the final compressed keys are repesented in our NewWorldBones table UpdateWorldBoneTransformRange( AnimSeq, BoneData, RefPose, PositionTracks, RotationTracks, ScaleTracks, BoneIndex, BoneIndex, false, NewWorldBones); } };
AHierarchicalLODVolume* FHierarchicalLODUtilities::CreateVolumeForLODActor(ALODActor* InLODActor, UWorld* InWorld) { FBox BoundingBox = InLODActor->GetComponentsBoundingBox(true); AHierarchicalLODVolume* Volume = InWorld->SpawnActor<AHierarchicalLODVolume>(AHierarchicalLODVolume::StaticClass(), FTransform(BoundingBox.GetCenter())); // this code builds a brush for the new actor Volume->PreEditChange(NULL); Volume->PolyFlags = 0; Volume->Brush = NewObject<UModel>(Volume, NAME_None, RF_Transactional); Volume->Brush->Initialize(nullptr, true); Volume->Brush->Polys = NewObject<UPolys>(Volume->Brush, NAME_None, RF_Transactional); Volume->GetBrushComponent()->Brush = Volume->Brush; Volume->BrushBuilder = NewObject<UCubeBuilder>(Volume, NAME_None, RF_Transactional); UCubeBuilder* CubeBuilder = static_cast<UCubeBuilder*>(Volume->BrushBuilder); CubeBuilder->X = BoundingBox.GetSize().X * 1.5f; CubeBuilder->Y = BoundingBox.GetSize().Y * 1.5f; CubeBuilder->Z = BoundingBox.GetSize().Z * 1.5f; Volume->BrushBuilder->Build(InWorld, Volume); FBSPOps::csgPrepMovingBrush(Volume); // Set the texture on all polys to NULL. This stops invisible textures // dependencies from being formed on volumes. if (Volume->Brush) { for (int32 poly = 0; poly < Volume->Brush->Polys->Element.Num(); ++poly) { FPoly* Poly = &(Volume->Brush->Polys->Element[poly]); Poly->Material = NULL; } } Volume->PostEditChange(); return Volume; }
void AGameplayPawn::AddSpriteAndUpdate(int x, int y) { GroupedGroundSprite->AddInstance(FTransform(FRotator(0.0f, 0.0f, 90.0f), FVector(GridToWorldX(x)*SCALE_FACTOR, GridToWorldY(y)*SCALE_FACTOR, 0.0f), FVector(1.0f, 1.0f, 1.0f)), GroundSprite->GetSprite(), true, FColor::White); }
void ACityMapMeshHolder::AddInstance(ECityMapMeshTag Tag, uint32 X, uint32 Y) { AddInstance(Tag, FTransform(GetTileLocation(X, Y))); }
//============================================================================= void UMotionControllerComponent::FViewExtension::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) { if (!MotionControllerComponent) { return; } FScopeLock ScopeLock(&CritSect); if (!MotionControllerComponent || MotionControllerComponent->bDisableLowLatencyUpdate || !CVarEnableMotionControllerLateUpdate.GetValueOnRenderThread()) { return; } // Poll state for the most recent controller transform FVector Position; FRotator Orientation; if (!MotionControllerComponent->PollControllerState(Position, Orientation)) { return; } if (LateUpdatePrimitives.Num()) { // Calculate the late update transform that will rebase all children proxies within the frame of reference const FTransform OldLocalToWorldTransform = MotionControllerComponent->CalcNewComponentToWorld(MotionControllerComponent->GetRelativeTransform()); const FTransform NewLocalToWorldTransform = MotionControllerComponent->CalcNewComponentToWorld(FTransform(Orientation, Position, MotionControllerComponent->GetComponentScale())); const FMatrix LateUpdateTransform = (OldLocalToWorldTransform.Inverse() * NewLocalToWorldTransform).ToMatrixWithScale(); // Apply delta to the affected scene proxies for (auto PrimitiveInfo : LateUpdatePrimitives) { FPrimitiveSceneInfo* RetrievedSceneInfo = InViewFamily.Scene->GetPrimitiveSceneInfo(*PrimitiveInfo.IndexAddress); FPrimitiveSceneInfo* CachedSceneInfo = PrimitiveInfo.SceneInfo; // If the retrieved scene info is different than our cached scene info then the primitive was removed from the scene if (CachedSceneInfo == RetrievedSceneInfo && CachedSceneInfo->Proxy) { CachedSceneInfo->Proxy->ApplyLateUpdateTransform(LateUpdateTransform); } } LateUpdatePrimitives.Reset(); } }
void FAnimNode_Fabrik::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { // IsValidToEvaluate validated our inputs, we don't need to check pre-requisites again. int32 const RootIndex = RootBone.BoneIndex; // Update EffectorLocation if it is based off a bone position FTransform CSEffectorTransform = FTransform(EffectorTransform); FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, CSEffectorTransform, EffectorTransformBone.BoneIndex, EffectorTransformSpace); FVector const CSEffectorLocation = CSEffectorTransform.GetLocation(); // @fixme - we need better to draw widgets and debug information in editor. #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (bEnableDebugDraw) { // Show end effector position. DrawDebugBox(SkelComp->GetWorld(), CSEffectorLocation, FVector(Precision), FColor::Green, true, 0.1f); DrawDebugCoordinateSystem(SkelComp->GetWorld(), CSEffectorLocation, CSEffectorTransform.GetRotation().Rotator(), 5.f, true, 0.1f); } #endif // Gather all bone indices between root and tip. TArray<int32> BoneIndices; { int32 BoneIndex = TipBone.BoneIndex; do { BoneIndices.Insert(BoneIndex, 0); BoneIndex = RequiredBones.GetParentBoneIndex(BoneIndex); } while (BoneIndex != RootIndex); BoneIndices.Insert(BoneIndex, 0); } // Maximum length of skeleton segment at full extension float MaximumReach = 0; // Gather transforms int32 const NumTransforms = BoneIndices.Num(); OutBoneTransforms.AddUninitialized(NumTransforms); // Gather chain links. These are non zero length bones. TArray<FABRIKChainLink> Chain; Chain.Reserve(NumTransforms); // Start with Root Bone { int32 const & RootBoneIndex = BoneIndices[0]; FTransform const BoneCSTransform = MeshBases.GetComponentSpaceTransform(RootBoneIndex); OutBoneTransforms[0] = FBoneTransform(RootBoneIndex, BoneCSTransform); Chain.Add(FABRIKChainLink(BoneCSTransform.GetLocation(), 0.f, RootBoneIndex, 0)); } // Go through remaining transforms for (int32 TransformIndex = 1; TransformIndex < NumTransforms; TransformIndex++) { int32 const & BoneIndex = BoneIndices[TransformIndex]; FTransform const BoneCSTransform = MeshBases.GetComponentSpaceTransform(BoneIndex); FVector const BoneCSPosition = BoneCSTransform.GetLocation(); OutBoneTransforms[TransformIndex] = FBoneTransform(BoneIndex, BoneCSTransform); // Calculate the combined length of this segment of skeleton float const BoneLength = FVector::Dist(BoneCSPosition, OutBoneTransforms[TransformIndex-1].Transform.GetLocation()); if (!FMath::IsNearlyZero(BoneLength)) { Chain.Add(FABRIKChainLink(BoneCSPosition, BoneLength, BoneIndex, TransformIndex)); MaximumReach += BoneLength; } else { // Mark this transform as a zero length child of the last link. // It will inherit position and delta rotation from parent link. FABRIKChainLink & ParentLink = Chain[Chain.Num()-1]; ParentLink.ChildZeroLengthTransformIndices.Add(TransformIndex); } } bool bBoneLocationUpdated = false; float const RootToTargetDist = FVector::Dist(Chain[0].Position, CSEffectorLocation); int32 const NumChainLinks = Chain.Num(); // FABRIK algorithm - bone translation calculation // If the effector is further away than the distance from root to tip, simply move all bones in a line from root to effector location if (RootToTargetDist > MaximumReach) { for (int32 LinkIndex = 1; LinkIndex < NumChainLinks; LinkIndex++) { FABRIKChainLink const & ParentLink = Chain[LinkIndex - 1]; FABRIKChainLink & CurrentLink = Chain[LinkIndex]; CurrentLink.Position = ParentLink.Position + (CSEffectorLocation - ParentLink.Position).GetUnsafeNormal() * CurrentLink.Length; } bBoneLocationUpdated = true; } else // Effector is within reach, calculate bone translations to position tip at effector location { int32 const TipBoneLinkIndex = NumChainLinks - 1; // Check distance between tip location and effector location float Slop = FVector::Dist(Chain[TipBoneLinkIndex].Position, CSEffectorLocation); if (Slop > Precision) { // Set tip bone at end effector location. Chain[TipBoneLinkIndex].Position = CSEffectorLocation; int32 IterationCount = 0; while ((Slop > Precision) && (IterationCount++ < MaxIterations)) { // "Forward Reaching" stage - adjust bones from end effector. for (int32 LinkIndex = TipBoneLinkIndex - 1; LinkIndex > 0; LinkIndex--) { FABRIKChainLink & CurrentLink = Chain[LinkIndex]; FABRIKChainLink const & ChildLink = Chain[LinkIndex + 1]; CurrentLink.Position = ChildLink.Position + (CurrentLink.Position - ChildLink.Position).GetUnsafeNormal() * ChildLink.Length; } // "Backward Reaching" stage - adjust bones from root. for (int32 LinkIndex = 1; LinkIndex < TipBoneLinkIndex; LinkIndex++) { FABRIKChainLink const & ParentLink = Chain[LinkIndex - 1]; FABRIKChainLink & CurrentLink = Chain[LinkIndex]; CurrentLink.Position = ParentLink.Position + (CurrentLink.Position - ParentLink.Position).GetUnsafeNormal() * CurrentLink.Length; } // Re-check distance between tip location and effector location // Since we're keeping tip on top of effector location, check with its parent bone. Slop = FMath::Abs(Chain[TipBoneLinkIndex].Length - FVector::Dist(Chain[TipBoneLinkIndex - 1].Position, CSEffectorLocation)); } // Place tip bone based on how close we got to target. { FABRIKChainLink const & ParentLink = Chain[TipBoneLinkIndex - 1]; FABRIKChainLink & CurrentLink = Chain[TipBoneLinkIndex]; CurrentLink.Position = ParentLink.Position + (CurrentLink.Position - ParentLink.Position).GetUnsafeNormal() * CurrentLink.Length; } bBoneLocationUpdated = true; } } // If we moved some bones, update bone transforms. if (bBoneLocationUpdated) { // First step: update bone transform positions from chain links. for (int32 LinkIndex = 0; LinkIndex < NumChainLinks; LinkIndex++) { FABRIKChainLink const & ChainLink = Chain[LinkIndex]; OutBoneTransforms[ChainLink.TransformIndex].Transform.SetTranslation(ChainLink.Position); // If there are any zero length children, update position of those int32 const NumChildren = ChainLink.ChildZeroLengthTransformIndices.Num(); for (int32 ChildIndex = 0; ChildIndex < NumChildren; ChildIndex++) { OutBoneTransforms[ChainLink.ChildZeroLengthTransformIndices[ChildIndex]].Transform.SetTranslation(ChainLink.Position); } } // FABRIK algorithm - re-orientation of bone local axes after translation calculation for (int32 LinkIndex = 0; LinkIndex < NumChainLinks - 1; LinkIndex++) { FABRIKChainLink const & CurrentLink = Chain[LinkIndex]; FABRIKChainLink const & ChildLink = Chain[LinkIndex + 1]; // Calculate pre-translation vector between this bone and child FVector const OldDir = (GetCurrentLocation(MeshBases, ChildLink.BoneIndex) - GetCurrentLocation(MeshBases, CurrentLink.BoneIndex)).GetUnsafeNormal(); // Get vector from the post-translation bone to it's child FVector const NewDir = (ChildLink.Position - CurrentLink.Position).GetUnsafeNormal(); // Calculate axis of rotation from pre-translation vector to post-translation vector FVector const RotationAxis = FVector::CrossProduct(OldDir, NewDir).GetSafeNormal(); float const RotationAngle = FMath::Acos(FVector::DotProduct(OldDir, NewDir)); FQuat const DeltaRotation = FQuat(RotationAxis, RotationAngle); // We're going to multiply it, in order to not have to re-normalize the final quaternion, it has to be a unit quaternion. checkSlow(DeltaRotation.IsNormalized()); // Calculate absolute rotation and set it FTransform& CurrentBoneTransform = OutBoneTransforms[CurrentLink.TransformIndex].Transform; CurrentBoneTransform.SetRotation(DeltaRotation * CurrentBoneTransform.GetRotation()); // Update zero length children if any int32 const NumChildren = CurrentLink.ChildZeroLengthTransformIndices.Num(); for (int32 ChildIndex = 0; ChildIndex < NumChildren; ChildIndex++) { FTransform& ChildBoneTransform = OutBoneTransforms[CurrentLink.ChildZeroLengthTransformIndices[ChildIndex]].Transform; ChildBoneTransform.SetRotation(DeltaRotation * ChildBoneTransform.GetRotation()); } } } // Special handling for tip bone's rotation. int32 const TipBoneTransformIndex = OutBoneTransforms.Num() - 1; switch (EffectorRotationSource) { case BRS_KeepLocalSpaceRotation: OutBoneTransforms[TipBoneTransformIndex].Transform = MeshBases.GetLocalSpaceTransform(BoneIndices[TipBoneTransformIndex]) * OutBoneTransforms[TipBoneTransformIndex - 1].Transform; break; case BRS_CopyFromTarget: OutBoneTransforms[TipBoneTransformIndex].Transform.SetRotation(CSEffectorTransform.GetRotation()); break; case BRS_KeepComponentSpaceRotation: // Don't change the orientation at all break; default: break; } }
bool UWorld::ComponentSweepSingle(struct FHitResult& OutHit,class UPrimitiveComponent* PrimComp, const FVector& Start, const FVector& End, const FRotator& Rot, const struct FComponentQueryParams& Params) const { OutHit.TraceStart = Start; OutHit.TraceEnd = End; if(GetPhysicsScene() == NULL) { return false; } if(PrimComp == NULL) { UE_LOG(LogCollision, Log, TEXT("ComponentSweepSingle : No PrimComp")); return false; } // if target is skeletalmeshcomponent and do not support singlebody physics if ( !PrimComp->ShouldTrackOverlaps() ) { UE_LOG(LogCollision, Log, TEXT("ComponentSweepSingle : (%s) Does not support skeletalmesh with Physics Asset and destructibles."), *PrimComp->GetPathName()); return false; } ECollisionChannel TraceChannel = PrimComp->GetCollisionObjectType(); #if WITH_PHYSX // if extent is 0, do line trace if (PrimComp->IsZeroExtent()) { return RaycastSingle(this, OutHit, Start, End, TraceChannel, Params, FCollisionResponseParams(PrimComp->GetCollisionResponseToChannels())); } PxRigidActor* PRigidActor = PrimComp->BodyInstance.GetPxRigidActor(); if(PRigidActor == NULL) { UE_LOG(LogCollision, Log, TEXT("ComponentSweepMulti : (%s) No physics data"), *PrimComp->GetPathName()); return false; } // Get all the shapes from the actor TArray<PxShape*, TInlineAllocator<8>> PShapes; PShapes.AddZeroed(PRigidActor->getNbShapes()); int32 NumShapes = PRigidActor->getShapes(PShapes.GetData(), PShapes.Num()); // calculate the test global pose of the actor PxTransform PGlobalStartPose = U2PTransform(FTransform(Start)); PxTransform PGlobalEndPose = U2PTransform(FTransform(End)); bool bHaveBlockingHit = false; PxQuat PGeomRot = U2PQuat(Rot.Quaternion()); // Iterate over each shape for(int32 ShapeIdx=0; ShapeIdx<PShapes.Num(); ShapeIdx++) { PxShape* PShape = PShapes[ShapeIdx]; check(PShape); // Calc shape global pose PxTransform PLocalShape = PShape->getLocalPose(); PxTransform PShapeGlobalStartPose = PGlobalStartPose.transform(PLocalShape); PxTransform PShapeGlobalEndPose = PGlobalEndPose.transform(PLocalShape); // consider localshape rotation for shape rotation PxQuat PShapeRot = PGeomRot * PLocalShape.q; GET_GEOMETRY_FROM_SHAPE(PGeom, PShape); if(PGeom != NULL) { // @todo UE4, this might not be the best behavior. If we're looking for the most closest, this have to change to save the result, and find the closest one or // any other options, right now if anything hits first, it will return if (GeomSweepSingle(this, *PGeom, PShapeRot, OutHit, P2UVector(PShapeGlobalStartPose.p), P2UVector(PShapeGlobalEndPose.p), TraceChannel, Params, FCollisionResponseParams(PrimComp->GetCollisionResponseToChannels()))) { bHaveBlockingHit = true; break; } } } return bHaveBlockingHit; #endif //WITH_PHYSX return false; }
bool UWorld::ComponentOverlapTest(class UPrimitiveComponent* PrimComp, const FVector& Pos, const FRotator& Rot, const struct FComponentQueryParams& Params) const { if(GetPhysicsScene() == NULL) { return false; } if(PrimComp == NULL) { UE_LOG(LogCollision, Log, TEXT("ComponentOverlapMulti : No PrimComp")); return false; } // if target is skeletalmeshcomponent and do not support singlebody physics, we don't support this yet // talk to @JG, SP, LH if ( !PrimComp->ShouldTrackOverlaps() ) { UE_LOG(LogCollision, Log, TEXT("ComponentOverlapMulti : (%s) Does not support skeletalmesh with Physics Asset and destructibles."), *PrimComp->GetPathName()); return false; } #if WITH_PHYSX ECollisionChannel TraceChannel = PrimComp->GetCollisionObjectType(); PxRigidActor* PRigidActor = PrimComp->BodyInstance.GetPxRigidActor(); if(PRigidActor == NULL) { UE_LOG(LogCollision, Log, TEXT("ComponentOverlapMulti : (%s) No physics data"), *PrimComp->GetPathName()); return false; } // calculate the test global pose of the actor PxTransform PTestGlobalPose = U2PTransform(FTransform(Rot, Pos)); // Get all the shapes from the actor TArray<PxShape*, TInlineAllocator<8>> PShapes; PShapes.AddZeroed(PRigidActor->getNbShapes()); int32 NumShapes = PRigidActor->getShapes(PShapes.GetData(), PShapes.Num()); // Iterate over each shape for(int32 ShapeIdx=0; ShapeIdx<PShapes.Num(); ShapeIdx++) { PxShape* PShape = PShapes[ShapeIdx]; check(PShape); // Calc shape global pose PxTransform PLocalPose = PShape->getLocalPose(); PxTransform PShapeGlobalPose = PTestGlobalPose.transform(PLocalPose); GET_GEOMETRY_FROM_SHAPE(PGeom, PShape); if(PGeom != NULL) { if( GeomOverlapTest(this, *PGeom, PShapeGlobalPose, TraceChannel, Params, FCollisionResponseParams(PrimComp->GetCollisionResponseToChannels()))) { // in this test, it only matters true or false. // if we found first true, we don't care next test anymore. return true; } } } #endif //WITH_PHYSX return false; }
void FDebugRenderSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const { QUICK_SCOPE_CYCLE_COUNTER( STAT_DebugRenderSceneProxy_GetDynamicMeshElements ); // Draw solid spheres struct FMaterialCache { FMaterialCache() : bUseFakeLight(false) {} FMaterialRenderProxy* operator[](FLinearColor Color) { FMaterialRenderProxy* MeshColor = NULL; const uint32 HashKey = GetTypeHash(Color); if (MeshColorInstances.Contains(HashKey)) { MeshColor = *MeshColorInstances.Find(HashKey); } else { if (bUseFakeLight && SolidMeshMaterial.IsValid()) { MeshColor = new(FMemStack::Get()) FColoredMaterialRenderProxy( SolidMeshMaterial->GetRenderProxy(false, false), Color, "GizmoColor" ); } else { MeshColor = new(FMemStack::Get()) FColoredMaterialRenderProxy(GEngine->DebugMeshMaterial->GetRenderProxy(false, false), Color); } MeshColorInstances.Add(HashKey, MeshColor); } return MeshColor; } void UseFakeLight(bool UseLight, class UMaterial* InMaterial) { bUseFakeLight = UseLight; SolidMeshMaterial = InMaterial; } TMap<uint32, FMaterialRenderProxy*> MeshColorInstances; TWeakObjectPtr<class UMaterial> SolidMeshMaterial; bool bUseFakeLight; }; FMaterialCache MaterialCache[2]; MaterialCache[1].UseFakeLight(true, SolidMeshMaterial.Get()); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex); // Draw Lines const int32 LinesNum = Lines.Num(); PDI->AddReserveLines(SDPG_World, LinesNum, false, false); for (const auto& CurrentLine : Lines) { PDI->DrawLine(CurrentLine.Start, CurrentLine.End, CurrentLine.Color, SDPG_World, CurrentLine.Thickness, 0, CurrentLine.Thickness > 0); } // Draw Dashed Lines for(int32 DashIdx=0; DashIdx<DashedLines.Num(); DashIdx++) { const FDashedLine& Dash = DashedLines[DashIdx]; DrawDashedLine(PDI, Dash.Start, Dash.End, Dash.Color, Dash.DashSize, SDPG_World); } // Draw Arrows const uint32 ArrowsNum = ArrowLines.Num(); PDI->AddReserveLines(SDPG_World, 5 * ArrowsNum, false, false); for (const auto& CurrentArrow : ArrowLines) { DrawLineArrow(PDI, CurrentArrow.Start, CurrentArrow.End, CurrentArrow.Color, 8.0f); } // Draw Stars for(int32 StarIdx=0; StarIdx<Stars.Num(); StarIdx++) { const FWireStar& Star = Stars[StarIdx]; DrawWireStar(PDI, Star.Position, Star.Size, Star.Color, SDPG_World); } // Draw Cylinders for(const auto& Cylinder : Cylinders) { if (DrawType == SolidAndWireMeshes || DrawType == WireMesh) { DrawWireCylinder(PDI, Cylinder.Base, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), Cylinder.Color, Cylinder.Radius, Cylinder.HalfHeight, (DrawType == SolidAndWireMeshes) ? 9 : 16, SDPG_World, DrawType == SolidAndWireMeshes ? 2 : 0, 0, true); } if (DrawType == SolidAndWireMeshes || DrawType == SolidMesh) { GetCylinderMesh(Cylinder.Base, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), Cylinder.Radius, Cylinder.HalfHeight, 16, MaterialCache[0][Cylinder.Color.WithAlpha(DrawAlpha)], SDPG_World, ViewIndex, Collector); } } // Draw Boxes for(const auto& Box : Boxes) { if (DrawType == SolidAndWireMeshes || DrawType == WireMesh) { DrawWireBox(PDI, Box.Transform.ToMatrixWithScale(), Box.Box, Box.Color, SDPG_World, DrawType == SolidAndWireMeshes ? 2 : 0, 0, true); } if (DrawType == SolidAndWireMeshes || DrawType == SolidMesh) { GetBoxMesh(FTransform(Box.Box.GetCenter()).ToMatrixNoScale() * Box.Transform.ToMatrixWithScale(), Box.Box.GetExtent(), MaterialCache[0][Box.Color.WithAlpha(DrawAlpha)], SDPG_World, ViewIndex, Collector); } } // Draw Boxes TArray<FVector> Verts; for (auto& CurrentCone : Cones) { if (DrawType == SolidAndWireMeshes || DrawType == WireMesh) { DrawWireCone(PDI, Verts, CurrentCone.ConeToWorld, 1, CurrentCone.Angle2, (DrawType == SolidAndWireMeshes) ? 9 : 16, CurrentCone.Color, SDPG_World, DrawType == SolidAndWireMeshes ? 2 : 0, 0, true); } if (DrawType == SolidAndWireMeshes || DrawType == SolidMesh) { GetConeMesh(CurrentCone.ConeToWorld, CurrentCone.Angle1, CurrentCone.Angle2, 16, MaterialCache[0][CurrentCone.Color.WithAlpha(DrawAlpha)], SDPG_World, ViewIndex, Collector); } } for (auto It = Spheres.CreateConstIterator(); It; ++It) { if (PointInView(It->Location, View)) { if (DrawType == SolidAndWireMeshes || DrawType == WireMesh) { DrawWireSphere(PDI, It->Location, It->Color.WithAlpha(255), It->Radius, 20, SDPG_World, DrawType == SolidAndWireMeshes ? 2 : 0, 0, true); } if (DrawType == SolidAndWireMeshes || DrawType == SolidMesh) { GetSphereMesh(It->Location, FVector(It->Radius), 20, 7, MaterialCache[0][It->Color.WithAlpha(DrawAlpha)], SDPG_World, false, ViewIndex, Collector); } } } for (auto It = Capsles.CreateConstIterator(); It; ++It) { if (PointInView(It->Location, View)) { if (DrawType == SolidAndWireMeshes || DrawType == WireMesh) { const float HalfAxis = FMath::Max<float>(It->HalfHeight - It->Radius, 1.f); const FVector BottomEnd = It->Location + It->Radius * It->Z; const FVector TopEnd = BottomEnd + (2 * HalfAxis) * It->Z; const float CylinderHalfHeight = (TopEnd - BottomEnd).Size() * 0.5; const FVector CylinderLocation = BottomEnd + CylinderHalfHeight * It->Z; DrawWireCapsule(PDI, CylinderLocation, It->X, It->Y, It->Z, It->Color, It->Radius, It->HalfHeight, (DrawType == SolidAndWireMeshes) ? 9 : 16, SDPG_World, DrawType == SolidAndWireMeshes ? 2 : 0, 0, true); } if (DrawType == SolidAndWireMeshes || DrawType == SolidMesh) { GetCapsuleMesh(It->Location, It->X, It->Y, It->Z, It->Color, It->Radius, It->HalfHeight, 16, MaterialCache[0][It->Color.WithAlpha(DrawAlpha)], SDPG_World, false, ViewIndex, Collector); } } } for (const auto& Mesh : Meshes) { FDynamicMeshBuilder MeshBuilder; MeshBuilder.AddVertices(Mesh.Vertices); MeshBuilder.AddTriangles(Mesh.Indices); MeshBuilder.GetMesh(FMatrix::Identity, MaterialCache[Mesh.Color.A == 255 ? 1 : 0][Mesh.Color.WithAlpha(DrawAlpha)], SDPG_World, false, false, ViewIndex, Collector); } } } }
FTransform UKismetMathLibrary::MakeTransform(FVector Translation, FRotator Rotation, FVector Scale) { return FTransform(Rotation,Translation,Scale); }
FTransform UKismetMathLibrary::Conv_VectorToTransform(FVector InTranslation) { return FTransform(InTranslation); }
bool FEdMode::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) { if(UsesPropertyWidgets()) { AActor* SelectedActor = GetFirstSelectedActorInstance(); if(SelectedActor != NULL && InViewportClient->GetCurrentWidgetAxis() != EAxisList::None) { GEditor->NoteActorMovement(); if (EditedPropertyName != TEXT("")) { FTransform LocalTM = FTransform::Identity; if(bEditedPropertyIsTransform) { LocalTM = GetPropertyValueByName<FTransform>(SelectedActor, EditedPropertyName, EditedPropertyIndex); } else { FVector LocalPos = GetPropertyValueByName<FVector>(SelectedActor, EditedPropertyName, EditedPropertyIndex); LocalTM = FTransform(LocalPos); } // Get actor transform (actor to world) FTransform ActorTM = SelectedActor->ActorToWorld(); // Calculate world transform FTransform WorldTM = LocalTM * ActorTM; // Calc delta specified by drag //FTransform DeltaTM(InRot.Quaternion(), InDrag); // Apply delta in world space WorldTM.SetTranslation(WorldTM.GetTranslation() + InDrag); WorldTM.SetRotation(InRot.Quaternion() * WorldTM.GetRotation()); // Convert new world transform back into local space LocalTM = WorldTM.GetRelativeTransform(ActorTM); // Apply delta scale LocalTM.SetScale3D(LocalTM.GetScale3D() + InScale); SelectedActor->PreEditChange(NULL); if(bEditedPropertyIsTransform) { SetPropertyValueByName<FTransform>(SelectedActor, EditedPropertyName, EditedPropertyIndex, LocalTM); } else { SetPropertyValueByName<FVector>(SelectedActor, EditedPropertyName, EditedPropertyIndex, LocalTM.GetLocation()); } SelectedActor->PostEditChange(); return true; } } } if( GetCurrentTool() ) { return GetCurrentTool()->InputDelta(InViewportClient,InViewport,InDrag,InRot,InScale); } return 0; }
/** custom instantiation of UpdateBoneAtom for FVectors */ template <> FTransform UpdateBoneAtom<FVector>(int32 BoneIndex, const FTransform& Atom, const FVector& Component) { return FTransform(Atom.GetRotation(), Component, FVector(1.0f)); }
/** custom instantiation of UpdateBoneAtom for FQuat */ template <> FTransform UpdateBoneAtom<FQuat>(int32 BoneIndex, const FTransform& Atom, const FQuat& Component) { return FTransform(Component, Atom.GetTranslation(), FVector(1.0f)); }
bool UWorld::ComponentSweepMulti(TArray<struct FHitResult>& OutHits, class UPrimitiveComponent* PrimComp, const FVector& Start, const FVector& End, const FRotator& Rot, const struct FComponentQueryParams& Params) const { if(GetPhysicsScene() == NULL) { return false; } if(PrimComp == NULL) { UE_LOG(LogCollision, Log, TEXT("ComponentSweepMulti : No PrimComp")); return false; } // if target is skeletalmeshcomponent and do not support singlebody physics if ( !PrimComp->ShouldTrackOverlaps() ) { UE_LOG(LogCollision, Log, TEXT("ComponentSweepMulti : (%s) Does not support skeletalmesh with Physics Asset and destructibles."), *PrimComp->GetPathName()); return false; } ECollisionChannel TraceChannel = PrimComp->GetCollisionObjectType(); #if WITH_PHYSX // if extent is 0, do line trace if (PrimComp->IsZeroExtent()) { return RaycastMulti(this, OutHits, Start, End, TraceChannel, Params, FCollisionResponseParams(PrimComp->GetCollisionResponseToChannels())); } PxRigidActor* PRigidActor = PrimComp->BodyInstance.GetPxRigidActor(); if(PRigidActor == NULL) { UE_LOG(LogCollision, Log, TEXT("ComponentSweepMulti : (%s) No physics data"), *PrimComp->GetPathName()); return false; } PxScene * const PScene = PRigidActor->getScene(); OutHits.Empty(); // Get all the shapes from the actor TArray<PxShape*, TInlineAllocator<8>> PShapes; { SCOPED_SCENE_READ_LOCK(PScene); PShapes.AddZeroed(PRigidActor->getNbShapes()); PRigidActor->getShapes(PShapes.GetData(), PShapes.Num()); } // calculate the test global pose of the actor PxTransform PGlobalStartPose = U2PTransform(FTransform(Start)); PxTransform PGlobalEndPose = U2PTransform(FTransform(End)); bool bHaveBlockingHit = false; PxQuat PGeomRot = U2PQuat(Rot.Quaternion()); // Iterate over each shape SCENE_LOCK_READ(PScene); for(int32 ShapeIdx=0; ShapeIdx<PShapes.Num(); ShapeIdx++) { PxShape* PShape = PShapes[ShapeIdx]; check(PShape); TArray<struct FHitResult> Hits; // Calc shape global pose PxTransform PLocalShape = PShape->getLocalPose(); PxTransform PShapeGlobalStartPose = PGlobalStartPose.transform(PLocalShape); PxTransform PShapeGlobalEndPose = PGlobalEndPose.transform(PLocalShape); // consider localshape rotation for shape rotation PxQuat PShapeRot = PGeomRot * PLocalShape.q; GET_GEOMETRY_FROM_SHAPE(PGeom, PShape); if(PGeom != NULL) { SCENE_UNLOCK_READ(PScene); if (GeomSweepMulti(this, *PGeom, PShapeRot, Hits, P2UVector(PShapeGlobalStartPose.p), P2UVector(PShapeGlobalEndPose.p), TraceChannel, Params, FCollisionResponseParams(PrimComp->GetCollisionResponseToChannels()))) { bHaveBlockingHit = true; } OutHits.Append(Hits); SCENE_LOCK_READ(PScene); } } SCENE_UNLOCK_READ(PScene); return bHaveBlockingHit; #endif //WITH_PHYSX return false; }
void FLevelUtils::ApplyLevelTransform( ULevel* Level, const FTransform& LevelTransform, bool bDoPostEditMove ) { bool bTransformActors = !LevelTransform.Equals(FTransform::Identity); if (bTransformActors) { if (!LevelTransform.GetRotation().IsIdentity()) { // If there is a rotation applied, then the relative precomputed bounds become invalid. Level->bTextureStreamingRotationChanged = true; } // Iterate over all actors in the level and transform them for( int32 ActorIndex=0; ActorIndex<Level->Actors.Num(); ActorIndex++ ) { AActor* Actor = Level->Actors[ActorIndex]; // Don't want to transform children they should stay relative to there parents. if( Actor && Actor->GetAttachParentActor() == NULL ) { // Has to modify root component directly as GetActorPosition is incorrect this early USceneComponent *RootComponent = Actor->GetRootComponent(); if (RootComponent) { RootComponent->SetRelativeLocationAndRotation( LevelTransform.TransformPosition(RootComponent->RelativeLocation), (FTransform(RootComponent->RelativeRotation) * LevelTransform).Rotator()); } } } #if WITH_EDITOR if( bDoPostEditMove ) { ApplyPostEditMove( Level ); } #endif // WITH_EDITOR Level->OnApplyLevelTransform.Broadcast(LevelTransform); } }
bool FInstancedStaticMeshSCSEditorCustomization::HandleViewportDrag(class USceneComponent* InSceneComponent, class USceneComponent* InComponentTemplate, const FVector& InDeltaTranslation, const FRotator& InDeltaRotation, const FVector& InDeltaScale, const FVector& InPivot) { check(InSceneComponent->IsA(UInstancedStaticMeshComponent::StaticClass())); UInstancedStaticMeshComponent* InstancedStaticMeshComponentScene = CastChecked<UInstancedStaticMeshComponent>(InSceneComponent); UInstancedStaticMeshComponent* InstancedStaticMeshComponentTemplate = CastChecked<UInstancedStaticMeshComponent>(InComponentTemplate); // transform pivot into component's space const FVector LocalPivot = InstancedStaticMeshComponentScene->GetComponentToWorld().InverseTransformPosition(InPivot); // Ensure that selected instances are up-to-date ValidateSelectedInstances(InstancedStaticMeshComponentScene); bool bMovedInstance = false; check(InstancedStaticMeshComponentScene->SelectedInstances.Num() == InstancedStaticMeshComponentScene->PerInstanceSMData.Num()); for(int32 InstanceIndex = 0; InstanceIndex < InstancedStaticMeshComponentScene->SelectedInstances.Num(); InstanceIndex++) { if (InstancedStaticMeshComponentScene->SelectedInstances[InstanceIndex] && InstancedStaticMeshComponentTemplate->PerInstanceSMData.IsValidIndex(InstanceIndex)) { const FMatrix& MatrixScene = InstancedStaticMeshComponentScene->PerInstanceSMData[InstanceIndex].Transform; FVector Translation = MatrixScene.GetOrigin(); FRotator Rotation = MatrixScene.Rotator(); FVector Scale = MatrixScene.GetScaleVector(); FVector NewTranslation = Translation; FRotator NewRotation = Rotation; FVector NewScale = Scale; if( !InDeltaRotation.IsZero() ) { NewRotation = FRotator( InDeltaRotation.Quaternion() * Rotation.Quaternion() ); NewTranslation -= LocalPivot; NewTranslation = FRotationMatrix( InDeltaRotation ).TransformPosition( NewTranslation ); NewTranslation += LocalPivot; } NewTranslation += InDeltaTranslation; if( !InDeltaScale.IsNearlyZero() ) { const FScaleMatrix ScaleMatrix( InDeltaScale ); FVector DeltaScale3D = ScaleMatrix.TransformPosition( Scale ); NewScale = Scale + DeltaScale3D; NewTranslation -= LocalPivot; NewTranslation += ScaleMatrix.TransformPosition( NewTranslation ); NewTranslation += LocalPivot; } InstancedStaticMeshComponentScene->UpdateInstanceTransform(InstanceIndex, FTransform(NewRotation, NewTranslation, NewScale)); InstancedStaticMeshComponentTemplate->UpdateInstanceTransform(InstanceIndex, FTransform(NewRotation, NewTranslation, NewScale)); bMovedInstance = true; } } return bMovedInstance; }
/** * Function for adding a box collision primitive to the supplied collision geometry based on the mesh of the box. * * We keep a list of triangle normals found so far. For each normal direction, * we should have 2 distances from the origin (2 parallel box faces). If the * mesh is a box, we should have 3 distinct normal directions, and 2 distances * found for each. The difference between these distances should be the box * dimensions. The 3 directions give us the key axes, and therefore the * box transformation matrix. This shouldn't rely on any vertex-ordering on * the triangles (normals are compared +ve & -ve). It also shouldn't matter * about how many triangles make up each side (but it will take longer). * We get the centre of the box from the centre of its AABB. */ void AddBoxGeomFromTris( const TArray<FPoly>& Tris, FKAggregateGeom* AggGeom, const TCHAR* ObjName ) { TArray<FPlaneInfo> Planes; for(int32 i=0; i<Tris.Num(); i++) { bool bFoundPlane = false; for(int32 j=0; j<Planes.Num() && !bFoundPlane; j++) { // if this triangle plane is already known... if( AreParallel( Tris[i].Normal, Planes[j].Normal ) ) { // Always use the same normal when comparing distances, to ensure consistent sign. float Dist = Tris[i].Vertices[0] | Planes[j].Normal; // we only have one distance, and its not that one, add it. if( Planes[j].DistCount == 1 && !AreEqual(Dist, Planes[j].PlaneDist[0]) ) { Planes[j].PlaneDist[1] = Dist; Planes[j].DistCount = 2; } // if we have a second distance, and its not that either, something is wrong. else if( Planes[j].DistCount == 2 && !AreEqual(Dist, Planes[j].PlaneDist[1]) ) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Found more than 2 planes with different distances."), ObjName); return; } bFoundPlane = true; } } // If this triangle does not match an existing plane, add to list. if(!bFoundPlane) { check( Planes.Num() < Tris.Num() ); FPlaneInfo NewPlane; NewPlane.Normal = Tris[i].Normal; NewPlane.DistCount = 1; NewPlane.PlaneDist[0] = Tris[i].Vertices[0] | NewPlane.Normal; Planes.Add(NewPlane); } } // Now we have our candidate planes, see if there are any problems // Wrong number of planes. if(Planes.Num() != 3) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Not very box-like (need 3 sets of planes)."), ObjName); return; } // If we don't have 3 pairs, we can't carry on. if((Planes[0].DistCount != 2) || (Planes[1].DistCount != 2) || (Planes[2].DistCount != 2)) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Incomplete set of planes (need 2 per axis)."), ObjName); return; } FMatrix BoxTM = FMatrix::Identity; BoxTM.SetAxis(0, Planes[0].Normal); BoxTM.SetAxis(1, Planes[1].Normal); // ensure valid TM by cross-product FVector ZAxis = Planes[0].Normal ^ Planes[1].Normal; if( !AreParallel(ZAxis, Planes[2].Normal) ) { UE_LOG(LogStaticMeshEdit, Log, TEXT("AddBoxGeomFromTris (%s): Box axes are not perpendicular."), ObjName); return; } BoxTM.SetAxis(2, ZAxis); // OBB centre == AABB centre. FBox Box(0); for(int32 i=0; i<Tris.Num(); i++) { Box += Tris[i].Vertices[0]; Box += Tris[i].Vertices[1]; Box += Tris[i].Vertices[2]; } BoxTM.SetOrigin( Box.GetCenter() ); // Allocate box in array FKBoxElem BoxElem; BoxElem.SetTransform( FTransform( BoxTM ) ); // distance between parallel planes is box edge lengths. BoxElem.X = FMath::Abs(Planes[0].PlaneDist[0] - Planes[0].PlaneDist[1]); BoxElem.Y = FMath::Abs(Planes[1].PlaneDist[0] - Planes[1].PlaneDist[1]); BoxElem.Z = FMath::Abs(Planes[2].PlaneDist[0] - Planes[2].PlaneDist[1]); AggGeom->BoxElems.Add(BoxElem); }
FTransform UAnimBone::GetTransform() { return FTransform(FQuat(Orientation), Position, Scale); }
void AFlarePlanetarium::SetupCelestialBody(CelestialBodyPosition* BodyPosition, double DisplayDistance, double DisplayRadius) { FVector PlayerShipLocation = FVector::ZeroVector; if (GetGame()->GetPC()->GetShipPawn()) { PlayerShipLocation = GetGame()->GetPC()->GetShipPawn()->GetActorLocation(); } #ifdef PLANETARIUM_DEBUG DrawDebugSphere(GetWorld(), FVector::ZeroVector, DisplayDistance /1000 , 32, FColor::Blue, false); PlayerShipLocation = FVector::ZeroVector; DisplayRadius /= 1000; DisplayDistance /= 1000; #endif BodyPosition->BodyComponent->SetRelativeLocation((DisplayDistance * BodyPosition->AlignedLocation.GetUnsafeNormal()).ToVector() + PlayerShipLocation); float Scale = DisplayRadius / 512; // Mesh size is 1024; BodyPosition->BodyComponent->SetRelativeScale3D(FPreciseVector(Scale).ToVector()); FTransform BaseRotation = FTransform(FRotator(0, 0 ,90)); FTransform TimeRotation = FTransform(FRotator(0, BodyPosition->TotalRotation, 0)); FQuat Rotation = (TimeRotation * BaseRotation).GetRotation(); // TODO Rotation float time interpolation BodyPosition->BodyComponent->SetRelativeRotation(FQuat::Identity); BodyPosition->BodyComponent->SetRelativeRotation(Rotation); // Apply sun direction to component UMaterialInstanceDynamic* ComponentMaterial = Cast<UMaterialInstanceDynamic>(BodyPosition->BodyComponent->GetMaterial(0)); if (!ComponentMaterial) { ComponentMaterial = UMaterialInstanceDynamic::Create(BodyPosition->BodyComponent->GetMaterial(0) , GetWorld()); BodyPosition->BodyComponent->SetMaterial(0, ComponentMaterial); } ComponentMaterial->SetVectorParameterValue("SunDirection", SunDirection.ToVector()); // Look for rings and orient them TArray<USceneComponent*> RingCandidates; BodyPosition->BodyComponent->GetChildrenComponents(true, RingCandidates); for (int32 ComponentIndex = 0; ComponentIndex < RingCandidates.Num(); ComponentIndex++) { UStaticMeshComponent* RingComponent = Cast<UStaticMeshComponent>(RingCandidates[ComponentIndex]); if (RingComponent && RingComponent->GetName().Contains("ring")) { // Get or create the material UMaterialInstanceDynamic* RingMaterial = Cast<UMaterialInstanceDynamic>(RingComponent->GetMaterial(0)); if (!RingMaterial) { RingMaterial = UMaterialInstanceDynamic::Create(RingComponent->GetMaterial(0), GetWorld()); RingComponent->SetMaterial(0, RingMaterial); } // Get world-space rotation angles for the ring and the sun float SunRotationPitch = FMath::RadiansToDegrees(FMath::Atan2(SunDirection.Z,SunDirection.X)) + 180; float RingRotationPitch = -BodyPosition->TotalRotation; // Feed params to the shader RingMaterial->SetScalarParameterValue("RingPitch", RingRotationPitch / 360); RingMaterial->SetScalarParameterValue("SunPitch", SunRotationPitch / 360); } } // Sun also rotates to track direction if (BodyPosition->Body == &Sun) { BodyPosition->BodyComponent->SetRelativeRotation(SunDirection.ToVector().Rotation()); } // Compute sun occlusion if (BodyPosition->Body != &Sun) { double OcclusionAngle = FPreciseMath::Asin(BodyPosition->Radius / BodyPosition->Distance); float BodyPhase = FMath::UnwindRadians(FMath::Atan2(BodyPosition->AlignedLocation.Z, BodyPosition->AlignedLocation.X)); float CenterAngularDistance = FMath::Abs(FMath::UnwindRadians(SunPhase - BodyPhase)); float AngleSum = (SunOcclusionAngle + OcclusionAngle); float AngleDiff = FMath::Abs(SunOcclusionAngle - OcclusionAngle); /*FLOGV("SetupCelestialBody %s BodyPhase = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(BodyPhase)); FLOGV("SetupCelestialBody %s SunPhase = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(SunPhase)); FLOGV("SetupCelestialBody %s OcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(OcclusionAngle)); FLOGV("SetupCelestialBody %s SunOcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(SunOcclusionAngle)); FLOGV("SetupCelestialBody %s AngleDiff = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(AngleDiff)); FLOGV("SetupCelestialBody %s CenterAngularDistance = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(CenterAngularDistance)); FLOGV("SetupCelestialBody %s AngleSum = %f", *BodyPosition->Body->Name.ToString(), FMath::RadiansToDegrees(AngleSum));*/ if (CenterAngularDistance < AngleSum) { // There is occlusion float OcclusionRatio; if (CenterAngularDistance < AngleDiff) { // Maximum occlusion OcclusionRatio = 1.0; } else { // Partial occlusion OcclusionRatio = (AngleSum - CenterAngularDistance) / (2* FMath::Min(SunOcclusionAngle, OcclusionAngle)); // OcclusionRatio = ((SunOcclusionAngle + OcclusionAngle) + FMath::Max(SunOcclusionAngle, OcclusionAngle) - FMath::Min(SunOcclusionAngle, OcclusionAngle)) / (2 * CenterAngularDistance); } //FLOGV("MoveCelestialBody %s OcclusionRatio = %f", *Body->Name, OcclusionRatio); // Now, find the surface occlusion float SunAngularSurface = PI*FMath::Square(SunOcclusionAngle); float MaxOcclusionAngularSurface = PI*FMath::Square(FMath::Min(SunOcclusionAngle, OcclusionAngle)); float MaxOcclusion = MaxOcclusionAngularSurface / SunAngularSurface; float Occlusion = OcclusionRatio * MaxOcclusion; /*FLOGV("SetupCelestialBody %s CenterAngularDistance = %f", *BodyPosition->Body->Name.ToString(), CenterAngularDistance); FLOGV("SetupCelestialBody %s SunOcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), SunOcclusionAngle); FLOGV("SetupCelestialBody %s OcclusionAngle = %f", *BodyPosition->Body->Name.ToString(), OcclusionAngle); FLOGV("SetupCelestialBody %s SunAngularSurface = %f", *BodyPosition->Body->Name.ToString(), SunAngularSurface); FLOGV("SetupCelestialBody %s MaxOcclusionAngularSurface = %f", *BodyPosition->Body->Name.ToString(), MaxOcclusionAngularSurface); FLOGV("SetupCelestialBody %s MaxOcclusion = %f", *BodyPosition->Body->Name.ToString(), MaxOcclusion); FLOGV("SetupCelestialBody %s Occlusion = %f", *BodyPosition->Body->Name.ToString(), Occlusion);*/ if (Occlusion > SunOcclusion) { // Keep only best occlusion SunOcclusion = Occlusion; } } } }
void UPhysicsHandleComponent::SetTargetLocationAndRotation(FVector NewLocation, FRotator NewRotation) { TargetTransform = FTransform(NewRotation, NewLocation); }