void FAnimNode_Trail::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); if( ChainLength < 2 ) { return; } // The incoming BoneIndex is the 'end' of the spline chain. We need to find the 'start' by walking SplineLength bones up hierarchy. // Fail if we walk past the root bone. const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); FCompactPoseBoneIndex WalkBoneIndex = TrailBone.GetCompactPoseIndex(BoneContainer); TArray<FCompactPoseBoneIndex> ChainBoneIndices; ChainBoneIndices.AddZeroed(ChainLength); ChainBoneIndices[ChainLength - 1] = WalkBoneIndex; for (int32 i = 1; i < ChainLength; i++) { // returns to avoid a crash // @TODO : shows an error message why failed if (WalkBoneIndex == 0) { return; } // Get parent bone. WalkBoneIndex = BoneContainer.GetParentBoneIndex(WalkBoneIndex); //Insert indices at the start of array, so that parents are before children in the array. int32 TransformIndex = ChainLength - (i + 1); ChainBoneIndices[TransformIndex] = WalkBoneIndex; } OutBoneTransforms.AddZeroed(ChainLength); // If we have >0 this frame, but didn't last time, record positions of all the bones. // Also do this if number has changed or array is zero. bool bHasValidStrength = (Alpha > 0.f); if(TrailBoneLocations.Num() != ChainLength || (bHasValidStrength && !bHadValidStrength)) { TrailBoneLocations.Empty(); TrailBoneLocations.AddZeroed(ChainLength); for(int32 i=0; i<ChainBoneIndices.Num(); i++) { FCompactPoseBoneIndex ChildIndex = ChainBoneIndices[i]; const FTransform& ChainTransform = MeshBases.GetComponentSpaceTransform(ChildIndex); TrailBoneLocations[i] = ChainTransform.GetTranslation(); } OldLocalToWorld = SkelComp->GetTransformMatrix(); } bHadValidStrength = bHasValidStrength; // transform between last frame and now. FMatrix OldToNewTM = OldLocalToWorld * SkelComp->GetTransformMatrix().InverseFast(); // Add fake velocity if present to all but root bone if(!FakeVelocity.IsZero()) { FVector FakeMovement = -FakeVelocity * ThisTimstep; if (bActorSpaceFakeVel && SkelComp->GetOwner()) { const FTransform BoneToWorld(SkelComp->GetOwner()->GetActorQuat(), SkelComp->GetOwner()->GetActorLocation()); FakeMovement = BoneToWorld.TransformVector(FakeMovement); } FakeMovement = SkelComp->GetTransformMatrix().InverseTransformVector(FakeMovement); // Then add to each bone for(int32 i=1; i<TrailBoneLocations.Num(); i++) { TrailBoneLocations[i] += FakeMovement; } } // Root bone of trail is not modified. FCompactPoseBoneIndex RootIndex = ChainBoneIndices[0]; const FTransform& ChainTransform = MeshBases.GetComponentSpaceTransform(RootIndex); OutBoneTransforms[0] = FBoneTransform(RootIndex, ChainTransform); TrailBoneLocations[0] = ChainTransform.GetTranslation(); // Starting one below head of chain, move bones. for(int32 i=1; i<ChainBoneIndices.Num(); i++) { // Parent bone position in component space. FCompactPoseBoneIndex ParentIndex = ChainBoneIndices[i - 1]; FVector ParentPos = TrailBoneLocations[i-1]; FVector ParentAnimPos = MeshBases.GetComponentSpaceTransform(ParentIndex).GetTranslation(); // Child bone position in component space. FCompactPoseBoneIndex ChildIndex = ChainBoneIndices[i]; FVector ChildPos = OldToNewTM.TransformPosition(TrailBoneLocations[i]); // move from 'last frames component' frame to 'this frames component' frame FVector ChildAnimPos = MeshBases.GetComponentSpaceTransform(ChildIndex).GetTranslation(); // Desired parent->child offset. FVector TargetDelta = (ChildAnimPos - ParentAnimPos); // Desired child position. FVector ChildTarget = ParentPos + TargetDelta; // Find vector from child to target FVector Error = ChildTarget - ChildPos; // Calculate how much to push the child towards its target float Correction = FMath::Clamp<float>(ThisTimstep * TrailRelaxation, 0.f, 1.f); // Scale correction vector and apply to get new world-space child position. TrailBoneLocations[i] = ChildPos + (Error * Correction); // If desired, prevent bones stretching too far. if(bLimitStretch) { float RefPoseLength = TargetDelta.Size(); FVector CurrentDelta = TrailBoneLocations[i] - TrailBoneLocations[i-1]; float CurrentLength = CurrentDelta.Size(); // If we are too far - cut it back (just project towards parent particle). if( (CurrentLength - RefPoseLength > StretchLimit) && CurrentLength > SMALL_NUMBER ) { FVector CurrentDir = CurrentDelta / CurrentLength; TrailBoneLocations[i] = TrailBoneLocations[i-1] + (CurrentDir * (RefPoseLength + StretchLimit)); } } // Modify child matrix OutBoneTransforms[i] = FBoneTransform(ChildIndex, MeshBases.GetComponentSpaceTransform(ChildIndex)); OutBoneTransforms[i].Transform.SetTranslation(TrailBoneLocations[i]); // Modify rotation of parent matrix to point at this one. // Calculate the direction that parent bone is currently pointing. FVector CurrentBoneDir = OutBoneTransforms[i-1].Transform.TransformVector( GetAlignVector(ChainBoneAxis, bInvertChainBoneAxis) ); CurrentBoneDir = CurrentBoneDir.GetSafeNormal(SMALL_NUMBER); // Calculate vector from parent to child. FVector NewBoneDir = FVector(OutBoneTransforms[i].Transform.GetTranslation() - OutBoneTransforms[i - 1].Transform.GetTranslation()).GetSafeNormal(SMALL_NUMBER); // Calculate a quaternion that gets us from our current rotation to the desired one. FQuat DeltaLookQuat = FQuat::FindBetween(CurrentBoneDir, NewBoneDir); FTransform DeltaTM( DeltaLookQuat, FVector(0.f) ); // Apply to the current parent bone transform. FTransform TmpMatrix = FTransform::Identity; TmpMatrix.CopyRotationPart(OutBoneTransforms[i - 1].Transform); TmpMatrix = TmpMatrix * DeltaTM; OutBoneTransforms[i - 1].Transform.CopyRotationPart(TmpMatrix); } // For the last bone in the chain, use the rotation from the bone above it. OutBoneTransforms[ChainLength - 1].Transform.CopyRotationPart(OutBoneTransforms[ChainLength - 2].Transform); // Update OldLocalToWorld OldLocalToWorld = SkelComp->GetTransformMatrix(); }
FVector UTKMathFunctionLibrary::GridSnap(FVector A, float Grid) { return A.GridSnap(Grid); }
FVector ABxtAsteroidSpawner::GetRandomSpawnDirection() const { const FVector direction(-1.0f, 0.0, 0.0f); const float angle = FMath::FRandRange(-45.0f, 45.0f); return direction.RotateAngleAxis(angle, FVector(0.0f, 0.0f, 1.0f)); }
void AGameplayDebuggingHUDComponent::DrawEQSData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && WITH_EQS PrintString(DefaultContext, TEXT("\n{green}EQS {white}[Use + key to switch query]\n")); if (DebugComponent->EQSLocalData.Num() == 0) { return; } const int32 EQSIndex = DebugComponent->EQSLocalData.Num() > 0 ? FMath::Clamp(DebugComponent->CurrentEQSIndex, 0, DebugComponent->EQSLocalData.Num() - 1) : INDEX_NONE; if (!DebugComponent->EQSLocalData.IsValidIndex(EQSIndex)) { return; } { int32 Index = 0; PrintString(DefaultContext, TEXT("{white}Queries: ")); for (auto CurrentQuery : DebugComponent->EQSLocalData) { if (EQSIndex == Index) { PrintString(DefaultContext, FString::Printf(TEXT("{green}%s, "), *CurrentQuery.Name)); } else { PrintString(DefaultContext, FString::Printf(TEXT("{yellow}%s, "), *CurrentQuery.Name)); } Index++; } PrintString(DefaultContext, TEXT("\n")); } auto& CurrentLocalData = DebugComponent->EQSLocalData[EQSIndex]; /** find and draw item selection */ int32 BestItemIndex = INDEX_NONE; { APlayerController* const MyPC = Cast<APlayerController>(PlayerOwner); FVector CamLocation; FVector FireDir; if (!MyPC->GetSpectatorPawn()) { FRotator CamRotation; MyPC->GetPlayerViewPoint(CamLocation, CamRotation); FireDir = CamRotation.Vector(); } else { FireDir = DefaultContext.Canvas->SceneView->GetViewDirection(); CamLocation = DefaultContext.Canvas->SceneView->ViewMatrices.ViewOrigin; } float bestAim = 0; for (int32 Index = 0; Index < CurrentLocalData.RenderDebugHelpers.Num(); ++Index) { auto& CurrentItem = CurrentLocalData.RenderDebugHelpers[Index]; const FVector AimDir = CurrentItem.Location - CamLocation; float FireDist = AimDir.SizeSquared(); FireDist = FMath::Sqrt(FireDist); float newAim = FireDir | AimDir; newAim = newAim / FireDist; if (newAim > bestAim) { BestItemIndex = Index; bestAim = newAim; } } if (BestItemIndex != INDEX_NONE) { DrawDebugSphere(World, CurrentLocalData.RenderDebugHelpers[BestItemIndex].Location, CurrentLocalData.RenderDebugHelpers[BestItemIndex].Radius, 8, FColor::Red, false); int32 FailedTestIndex = CurrentLocalData.RenderDebugHelpers[BestItemIndex].FailedTestIndex; if (FailedTestIndex != INDEX_NONE) { PrintString(DefaultContext, FString::Printf(TEXT("{red}Selected item failed with test %d: {yellow}%s {LightBlue}(%s)\n") , FailedTestIndex , *CurrentLocalData.Tests[FailedTestIndex].ShortName , *CurrentLocalData.Tests[FailedTestIndex].Detailed )); PrintString(DefaultContext, FString::Printf(TEXT("{white}'%s' with score %3.3f\n\n"), *CurrentLocalData.RenderDebugHelpers[BestItemIndex].AdditionalInformation, CurrentLocalData.RenderDebugHelpers[BestItemIndex].FailedScore)); } } } PrintString(DefaultContext, FString::Printf(TEXT("{white}Timestamp: {yellow}%.3f (%.2fs ago)\n") , CurrentLocalData.Timestamp, PC->GetWorld()->GetTimeSeconds() - CurrentLocalData.Timestamp )); PrintString(DefaultContext, FString::Printf(TEXT("{white}Query ID: {yellow}%d\n") , CurrentLocalData.Id )); PrintString(DefaultContext, FString::Printf(TEXT("{white}Query contains %d options: "), CurrentLocalData.Options.Num())); for (int32 OptionIndex = 0; OptionIndex < CurrentLocalData.Options.Num(); ++OptionIndex) { if (OptionIndex == CurrentLocalData.UsedOption) { PrintString(DefaultContext, FString::Printf(TEXT("{green}%s, "), *CurrentLocalData.Options[OptionIndex])); } else { PrintString(DefaultContext, FString::Printf(TEXT("{yellow}%s, "), *CurrentLocalData.Options[OptionIndex])); } } PrintString(DefaultContext, TEXT("\n")); const float RowHeight = 20.0f; const int32 NumTests = CurrentLocalData.Tests.Num(); if (CurrentLocalData.NumValidItems > 0 && GetDebuggingReplicator()->EnableEQSOnHUD ) { // draw test weights for best X items const int32 NumItems = CurrentLocalData.Items.Num(); FCanvasTileItem TileItem(FVector2D(0, 0), GWhiteTexture, FVector2D(Canvas->SizeX, RowHeight), FLinearColor::Black); FLinearColor ColorOdd(0, 0, 0, 0.6f); FLinearColor ColorEven(0, 0, 0.4f, 0.4f); TileItem.BlendMode = SE_BLEND_Translucent; // table header { DefaultContext.CursorY += RowHeight; const float HeaderY = DefaultContext.CursorY + 3.0f; TileItem.SetColor(ColorOdd); DrawItem(DefaultContext, TileItem, 0, DefaultContext.CursorY); float HeaderX = DefaultContext.CursorX; PrintString(DefaultContext, FColor::Yellow, FString::Printf(TEXT("Num items: %d"), CurrentLocalData.NumValidItems), HeaderX, HeaderY); HeaderX += ItemDescriptionWidth; PrintString(DefaultContext, FColor::White, TEXT("Score"), HeaderX, HeaderY); HeaderX += ItemScoreWidth; for (int32 TestIdx = 0; TestIdx < NumTests; TestIdx++) { PrintString(DefaultContext, FColor::White, FString::Printf(TEXT("Test %d"), TestIdx), HeaderX, HeaderY); HeaderX += TestScoreWidth; } DefaultContext.CursorY += RowHeight; } // valid items for (int32 Idx = 0; Idx < NumItems; Idx++) { TileItem.SetColor((Idx % 2) ? ColorOdd : ColorEven); DrawItem(DefaultContext, TileItem, 0, DefaultContext.CursorY); DrawEQSItemDetails(Idx, DebugComponent); DefaultContext.CursorY += RowHeight; } DefaultContext.CursorY += RowHeight; } // test description PrintString(DefaultContext, TEXT("All tests from used option:\n")); for (int32 TestIdx = 0; TestIdx < NumTests; TestIdx++) { FString TestDesc = FString::Printf(TEXT("{white}Test %d = {yellow}%s {LightBlue}(%s)\n"), TestIdx, *CurrentLocalData.Tests[TestIdx].ShortName, *CurrentLocalData.Tests[TestIdx].Detailed); PrintString(DefaultContext, TestDesc); } #endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST) }
static void dLogSum(double g, const FVector &v, FVector &r) { assert(v.size() <= r.size()); dLogSum(g, v, r, v.size()); }
/** * @return true if the delta was handled by this editor mode tool. */ bool FModeTool_Texture::InputDelta(FEditorViewportClient* InViewportClient,FViewport* InViewport,FVector& InDrag,FRotator& InRot,FVector& InScale) { if( InViewportClient->GetCurrentWidgetAxis() == EAxisList::None ) { return false; } // calculate delta drag for this tick for the call to GEditor->polyTexPan below which is using relative (delta) mode FVector deltaDrag = InDrag; if (true == InViewportClient->IsPerspective()) { // perspective viewports pass the absolute drag so subtract the last tick's drag value to get the delta deltaDrag -= PreviousInputDrag; } PreviousInputDrag = InDrag; if( !deltaDrag.IsZero() ) { // Ensure each polygon has a unique base point index. for( FConstLevelIterator Iterator = InViewportClient->GetWorld()->GetLevelIterator(); Iterator; ++Iterator ) { UModel* Model = (*Iterator)->Model; for(int32 SurfaceIndex = 0;SurfaceIndex < Model->Surfs.Num();SurfaceIndex++) { FBspSurf& Surf = Model->Surfs[SurfaceIndex]; if(Surf.PolyFlags & PF_Selected) { const FVector Base = Model->Points[Surf.pBase]; Surf.pBase = Model->Points.Add(Base); } } FMatrix Mat = GLevelEditorModeTools().GetCustomDrawingCoordinateSystem(); FVector UVW = Mat.InverseTransformVector( deltaDrag ); // InverseTransformNormal because Mat is the transform from the surface/widget's coords to world coords GEditor->polyTexPan( Model, UVW.X, UVW.Y, 0 ); // 0 is relative mode because UVW is made from deltaDrag - the user input since the last tick } } if( !InRot.IsZero() ) { const FRotationMatrix RotationMatrix( InRot ); // Ensure each polygon has unique texture vector indices. for ( TSelectedSurfaceIterator<> It(InViewportClient->GetWorld()) ; It ; ++It ) { FBspSurf* Surf = *It; UModel* Model = It.GetModel(); FVector TextureU = Model->Vectors[Surf->vTextureU]; FVector TextureV = Model->Vectors[Surf->vTextureV]; TextureU = RotationMatrix.TransformPosition( TextureU ); TextureV = RotationMatrix.TransformPosition( TextureV ); Surf->vTextureU = Model->Vectors.Add(TextureU); Surf->vTextureV = Model->Vectors.Add(TextureV); const bool bUpdateTexCoords = true; const bool bOnlyRefreshSurfaceMaterials = true; GEditor->polyUpdateMaster(Model, It.GetSurfaceIndex(), bUpdateTexCoords, bOnlyRefreshSurfaceMaterials); } } if( !InScale.IsZero() ) { float ScaleU = InScale.X / GEditor->GetGridSize(); float ScaleV = InScale.Y / GEditor->GetGridSize(); ScaleU = 1.f - (ScaleU / 100.f); ScaleV = 1.f - (ScaleV / 100.f); // Ensure each polygon has unique texture vector indices. for( FConstLevelIterator Iterator = InViewportClient->GetWorld()->GetLevelIterator(); Iterator; ++Iterator ) { UModel* Model = (*Iterator)->Model; for(int32 SurfaceIndex = 0;SurfaceIndex < Model->Surfs.Num();SurfaceIndex++) { FBspSurf& Surf = Model->Surfs[SurfaceIndex]; if(Surf.PolyFlags & PF_Selected) { const FVector TextureU = Model->Vectors[Surf.vTextureU]; const FVector TextureV = Model->Vectors[Surf.vTextureV]; Surf.vTextureU = Model->Vectors.Add(TextureU); Surf.vTextureV = Model->Vectors.Add(TextureV); } } GEditor->polyTexScale( Model, ScaleU, 0.f, 0.f, ScaleV, false ); } } return true; }
void UFlareSpacecraftDamageSystem::OnCollision(class AActor* Other, FVector HitLocation, FVector NormalImpulse) { // If receive hit from over actor, like a ship we must apply collision damages. // The applied damage energy is 0.2% of the kinetic energy of the other actor. The kinetic // energy is calculated from the relative speed between the 2 actors, and only with the relative // speed projected in the axis of the collision normal: if 2 very fast ship only slightly touch, // only few energy will be decipated by the impact. // // The damages are applied only to the current actor, the ReceiveHit method of the other actor // will also call an it will apply its collision damages itself. // If the other actor is a projectile, specific weapon damage code is done in the projectile hit // handler: in this case we ignore the collision AFlareShell* OtherProjectile = Cast<AFlareShell>(Other); if (OtherProjectile) { return; } // No primitive component, ignore UPrimitiveComponent* OtherRoot = Cast<UPrimitiveComponent>(Other->GetRootComponent()); if (!OtherRoot) { return; } // Ignore debris AStaticMeshActor* OtherActor = Cast<AStaticMeshActor>(Other); if (OtherActor) { if (OtherActor->GetName().StartsWith("Debris")) { return; } } // Relative velocity FVector DeltaVelocity = ((OtherRoot->GetPhysicsLinearVelocity() - Spacecraft->Airframe->GetPhysicsLinearVelocity()) / 100); // Compute the relative velocity in the impact axis then compute kinetic energy /*float ImpactSpeed = DeltaVelocity.Size(); float ImpactMass = FMath::Min(Spacecraft->GetSpacecraftMass(), OtherRoot->GetMass()); */ //200 m /s -> 6301.873047 * 20000 -> 300 / 2 damage float ImpactSpeed = 0; float ImpactEnergy = 0; float ImpactMass = Spacecraft->GetSpacecraftMass(); // Check if the mass was set and is valid if (ImpactMass > KINDA_SMALL_NUMBER) { ImpactSpeed = NormalImpulse.Size() / (ImpactMass * 100.f); ImpactEnergy = ImpactMass * ImpactSpeed / 8402.f; } float Radius = 0.2 + FMath::Sqrt(ImpactEnergy) * 0.11; //FLOGV("OnCollision %s", *Spacecraft->GetImmatriculation().ToString()); //FLOGV(" OtherRoot->GetPhysicsLinearVelocity()=%s", *OtherRoot->GetPhysicsLinearVelocity().ToString()); //FLOGV(" OtherRoot->GetPhysicsLinearVelocity().Size()=%f", OtherRoot->GetPhysicsLinearVelocity().Size()); //FLOGV(" Spacecraft->Airframe->GetPhysicsLinearVelocity()=%s", *Spacecraft->Airframe->GetPhysicsLinearVelocity().ToString()); //FLOGV(" Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()=%f", Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()); //FLOGV(" dot=%f", FVector::DotProduct(DeltaVelocity.GetUnsafeNormal(), HitNormal.GetUnsafeNormal())); /*FLOGV(" DeltaVelocity=%s", *DeltaVelocity.ToString()); FLOGV(" ImpactSpeed=%f", ImpactSpeed); FLOGV(" ImpactMass=%f", ImpactMass); FLOGV(" ImpactEnergy=%f", ImpactEnergy); FLOGV(" Radius=%f", Radius);*/ bool HasHit = false; FHitResult BestHitResult; float BestHitDistance = 0; for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]); if (Component) { FHitResult HitResult(ForceInit); FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; Component->LineTraceComponent(HitResult, HitLocation, HitLocation + Spacecraft->GetLinearVelocity().GetUnsafeNormal() * 10000, TraceParams); if (HitResult.Actor.IsValid()){ float HitDistance = (HitResult.Location - HitLocation).Size(); if (!HasHit || HitDistance < BestHitDistance) { BestHitDistance = HitDistance; BestHitResult = HitResult; } //FLOGV("Collide hit %s at a distance=%f", *Component->GetReadableName(), HitDistance); HasHit = true; } } } if (HasHit) { //DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Magenta, true); } else { int32 BestComponentIndex = -1; BestHitDistance = 0; for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]); if (Component) { float ComponentDistance = (Component->GetComponentLocation() - HitLocation).Size(); if (BestComponentIndex == -1 || BestHitDistance > ComponentDistance) { BestComponentIndex = ComponentIndex; BestHitDistance = ComponentDistance; } } } UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[BestComponentIndex]); FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; Component->LineTraceComponent(BestHitResult, HitLocation, Component->GetComponentLocation(), TraceParams); //DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Yellow, true); } AFlareSpacecraft* OtherSpacecraft = Cast<AFlareSpacecraft>(Other); UFlareCompany* DamageSource = NULL; if (OtherSpacecraft) { DamageSource = OtherSpacecraft->GetParent()->GetCompany(); LastDamageCauser = OtherSpacecraft; } else { LastDamageCauser = NULL; } ApplyDamage(ImpactEnergy, Radius, BestHitResult.Location, EFlareDamage::DAM_Collision, DamageSource); }
static FVector FindTriMeshOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal) { if (IsInvalidFaceIndex(PHit.faceIndex)) { return InNormal; } PxTriangleMeshGeometry PTriMeshGeom; bool bSuccess = PHit.shape->getTriangleMeshGeometry(PTriMeshGeom); check(bSuccess); //this function should only be called when we have a trimesh if (PTriMeshGeom.triangleMesh) { check(PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles()); const PxU32 TriIndex = PHit.faceIndex; const void* Triangles = PTriMeshGeom.triangleMesh->getTriangles(); // Grab triangle indices that we hit int32 I0, I1, I2; if (PTriMeshGeom.triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES) { PxU16* P16BitIndices = (PxU16*)Triangles; I0 = P16BitIndices[(TriIndex * 3) + 0]; I1 = P16BitIndices[(TriIndex * 3) + 1]; I2 = P16BitIndices[(TriIndex * 3) + 2]; } else { PxU32* P32BitIndices = (PxU32*)Triangles; I0 = P32BitIndices[(TriIndex * 3) + 0]; I1 = P32BitIndices[(TriIndex * 3) + 1]; I2 = P32BitIndices[(TriIndex * 3) + 2]; } // Get verts we hit (local space) const PxVec3* PVerts = PTriMeshGeom.triangleMesh->getVertices(); const PxVec3 V0 = PVerts[I0]; const PxVec3 V1 = PVerts[I1]; const PxVec3 V2 = PVerts[I2]; // Find normal of triangle (local space), and account for non-uniform scale const PxVec3 PTempNormal = ((V1 - V0).cross(V2 - V0)).getNormalized(); const PxVec3 PLocalTriNormal = TransformNormalToShapeSpace(PTriMeshGeom.scale, PTempNormal); // Convert to world space const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor); const PxVec3 PWorldTriNormal = PShapeWorldPose.rotate(PLocalTriNormal); FVector OutNormal = P2UVector(PWorldTriNormal); if (PTriMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) { //double sided mesh so we need to consider direction of query const float sign = FVector::DotProduct(OutNormal, TraceDirectionDenorm) > 0.f ? -1.f : 1.f; OutNormal *= sign; } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!OutNormal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is TriangleMesh): %s (V0:%s, V1:%s, V2:%s)"), *OutNormal.ToString(), *P2UVector(V0).ToString(), *P2UVector(V1).ToString(), *P2UVector(V2).ToString()); UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString()); } #endif return OutNormal; } return InNormal; }
/* Validate Normal of OutResult. We're on hunt for invalid normal */ static void CheckHitResultNormal(const FHitResult& OutResult, const TCHAR* Message, const FVector& Start=FVector::ZeroVector, const FVector& End = FVector::ZeroVector, const PxGeometry* const Geom=NULL) { if(!OutResult.bStartPenetrating && !OutResult.Normal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("(%s) Non-normalized OutResult.Normal from hit conversion: %s (Component- %s)"), Message, *OutResult.Normal.ToString(), *GetNameSafe(OutResult.Component.Get())); UE_LOG(LogPhysics, Warning, TEXT("Start Loc(%s), End Loc(%s), Hit Loc(%s), ImpactNormal(%s)"), *Start.ToString(), *End.ToString(), *OutResult.Location.ToString(), *OutResult.ImpactNormal.ToString() ); if (Geom != NULL) { if (Geom->getType() == PxGeometryType::eCAPSULE) { const PxCapsuleGeometry * Capsule = (PxCapsuleGeometry*)Geom; UE_LOG(LogPhysics, Warning, TEXT("Capsule radius (%f), Capsule Halfheight (%f)"), Capsule->radius, Capsule->halfHeight); } } ensure(OutResult.Normal.IsNormalized()); } }
void UInterpToMovementComponent::AddControlPointPosition(FVector Pos) { UE_LOG(LogInterpToMovementComponent, Warning, TEXT("Pos:%s"),*Pos.ToString()); ControlPoints.Add( FInterpControlPoint(Pos)); }
//RickH - We could probably significantly improve speed if we put separate Z checks in place and did everything else in 2D. FVector UAvoidanceManager::GetAvoidanceVelocity_Internal(const FNavAvoidanceData& inAvoidanceData, float DeltaTime, int32* inIgnoreThisUID) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!bSystemActive) { return inAvoidanceData.Velocity; } #endif if (DeltaTime <= 0.0f) { return inAvoidanceData.Velocity; } FVector ReturnVelocity = inAvoidanceData.Velocity * DeltaTime; float MaxSpeed = ReturnVelocity.Size2D(); float CurrentTime; UWorld* MyWorld = Cast<UWorld>(GetOuter()); if (MyWorld) { CurrentTime = MyWorld->TimeSeconds; } else { //No world? OK, just quietly back out and don't alter anything. return inAvoidanceData.Velocity; } bool Unobstructed = true; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) bool DebugMode = IsDebugOnForAll() || (inIgnoreThisUID ? IsDebugOnForUID(*inIgnoreThisUID) : false); #endif //If we're moving very slowly, just push forward. Not sure it's worth avoiding at this speed, though I could be wrong. if (MaxSpeed < 0.01f) { return inAvoidanceData.Velocity; } AllCones.Empty(AllCones.Max()); //DrawDebugDirectionalArrow(GetWorld(), inAvoidanceData.Center, inAvoidanceData.Center + inAvoidanceData.Velocity, 2.5f, FColor(0,255,255), true, 0.05f, SDPG_MAX); for (auto& AvoidanceObj : AvoidanceObjects) { if ((inIgnoreThisUID) && (*inIgnoreThisUID == AvoidanceObj.Key)) { continue; } FNavAvoidanceData& OtherObject = AvoidanceObj.Value; // //Start with a few fast-rejects // //If the object has expired, ignore it if (OtherObject.ShouldBeIgnored()) { continue; } //If other object is not in avoided group, ignore it if (inAvoidanceData.ShouldIgnoreGroup(OtherObject.GroupMask)) { continue; } //RickH - We should have a max-radius parameter/option here, so I'm just going to hardcode one for now. //if ((OtherObject.Radius + _AvoidanceData.Radius + MaxSpeed + OtherObject.Velocity.Size2D()) < FVector::Dist(OtherObject.Center, _AvoidanceData.Center)) if (FVector2D(OtherObject.Center - inAvoidanceData.Center).SizeSquared() > FMath::Square(TestRadius2D)) { continue; } if (FMath::Abs(OtherObject.Center.Z - inAvoidanceData.Center.Z) > TestHeightDifference) { continue; } //If we are moving away from the obstacle, ignore it. Even if we're the slower one, let the other obstacle path around us. if ((ReturnVelocity | (OtherObject.Center - inAvoidanceData.Center)) <= 0.0f) { continue; } //Create data for the avoidance routine { FVector PointAWorld = inAvoidanceData.Center; FVector PointBRelative = OtherObject.Center - PointAWorld; FVector TowardB, SidewaysFromB; FVector VelAdjustment; FVector VelAfterAdjustment; float RadiusB = OtherObject.Radius + inAvoidanceData.Radius; PointBRelative.Z = 0.0f; TowardB = PointBRelative.SafeNormal2D(); //Don't care about height for this game. Rough height-checking will come in later, but even then it will be acceptable to do this. if (TowardB.IsZero()) { //Already intersecting, or aligned vertically, scrap this whole object. continue; } SidewaysFromB.Set(-TowardB.Y, TowardB.X, 0.0f); //Build collision cone (two planes) and store for later use. We might consider some fast rejection here to see if we can skip the cone entirely. //RickH - If we built these cones in 2D, we could avoid all the cross-product matrix stuff and just use (y, -x) 90-degree rotation. { FVector PointPlane[2]; FVector EffectiveVelocityB; FVelocityAvoidanceCone NewCone; //Use RVO (as opposed to VO) only for objects that are not overridden to max weight AND that are currently moving toward us. if ((OtherObject.OverrideWeightTime <= CurrentTime) && ((OtherObject.Velocity|PointBRelative) < 0.0f)) { float OtherWeight = (OtherObject.Weight + (1.0f - inAvoidanceData.Weight)) * 0.5f; //Use the average of what the other wants to be and what we want it to be. EffectiveVelocityB = ((inAvoidanceData.Velocity * (1.0f - OtherWeight)) + (OtherObject.Velocity * OtherWeight)) * DeltaTime; } else { EffectiveVelocityB = OtherObject.Velocity * DeltaTime; //This is equivalent to VO (not RVO) because the other object is not going to reciprocate our avoidance. } checkSlow(EffectiveVelocityB.Z == 0.0f); //Make the left plane PointPlane[0] = EffectiveVelocityB + (PointBRelative + (SidewaysFromB * RadiusB)); PointPlane[1].Set(PointPlane[0].X, PointPlane[0].Y, PointPlane[0].Z + 100.0f); NewCone.ConePlane[0] = FPlane(EffectiveVelocityB, PointPlane[0], PointPlane[1]); //First point is relative to A, which is ZeroVector in this implementation checkSlow((((PointBRelative+EffectiveVelocityB)|NewCone.ConePlane[0]) - NewCone.ConePlane[0].W) > 0.0f); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (DebugMode) { // DrawDebugDirectionalArrow(MyWorld, EffectiveVelocityB + PointAWorld, PointPlane[0] + PointAWorld, 50.0f, FColor(64,64,64), true, 0.05f, SDPG_MAX); // DrawDebugLine(MyWorld, PointAWorld, EffectiveVelocityB + PointAWorld, FColor(64,64,64), true, 0.05f, SDPG_MAX, 5.0f); } #endif //Make the right plane PointPlane[0] = EffectiveVelocityB + (PointBRelative - (SidewaysFromB * RadiusB)); PointPlane[1].Set(PointPlane[0].X, PointPlane[0].Y, PointPlane[0].Z - 100.0f); NewCone.ConePlane[1] = FPlane(EffectiveVelocityB, PointPlane[0], PointPlane[1]); //First point is relative to A, which is ZeroVector in this implementation checkSlow((((PointBRelative+EffectiveVelocityB)|NewCone.ConePlane[1]) - NewCone.ConePlane[1].W) > 0.0f); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (DebugMode) { // DrawDebugDirectionalArrow(MyWorld, EffectiveVelocityB + PointAWorld, PointPlane[0] + PointAWorld, 50.0f, FColor(64,64,64), true, 0.05f, SDPG_MAX); } #endif if ((((ReturnVelocity|NewCone.ConePlane[0]) - NewCone.ConePlane[0].W) > 0.0f) && (((ReturnVelocity|NewCone.ConePlane[1]) - NewCone.ConePlane[1].W) > 0.0f)) { Unobstructed = false; } AllCones.Add(NewCone); } } } if (Unobstructed) { //Trivial case, our ideal velocity is available. return inAvoidanceData.Velocity; } //Find a good velocity that isn't inside a cone. if (AllCones.Num()) { float AngleCurrent; float AngleF = ReturnVelocity.HeadingAngle(); float BestScore = 0.0f; float BestScorePotential; FVector BestVelocity = FVector::ZeroVector; //Worst case is we just stand completely still. Should we also allow backing up? Should we test standing still? const int AngleCount = 4; //Every angle will be tested both right and left. float AngleOffset[AngleCount] = {FMath::DegreesToRadians<float>(23.0f), FMath::DegreesToRadians<float>(40.0f), FMath::DegreesToRadians<float>(55.0f), FMath::DegreesToRadians<float>(85.0f)}; FVector AngleVector[AngleCount<<1]; //Determine check angles for (int i = 0; i < AngleCount; ++i) { AngleCurrent = AngleF - AngleOffset[i]; AngleVector[(i<<1)].Set(FMath::Cos(AngleCurrent), FMath::Sin(AngleCurrent), 0.0f); AngleCurrent = AngleF + AngleOffset[i]; AngleVector[(i<<1) + 1].Set(FMath::Cos(AngleCurrent), FMath::Sin(AngleCurrent), 0.0f); } //Sample velocity-space destination points and drag them back to form lines for (int AngleToCheck = 0; AngleToCheck < (AngleCount<<1); ++AngleToCheck) { FVector VelSpacePoint = AngleVector[AngleToCheck] * MaxSpeed; //Skip testing if we know we can't possibly get a better score than what we have already. //Note: This assumes the furthest point is the highest-scoring value (i.e. VelSpacePoint is not moving backward relative to ReturnVelocity) BestScorePotential = (VelSpacePoint|ReturnVelocity) * (VelSpacePoint|VelSpacePoint); if (BestScorePotential > BestScore) { FVector CandidateVelocity = AvoidCones(AllCones, FVector::ZeroVector, VelSpacePoint, AllCones.Num()); float CandidateScore = (CandidateVelocity|ReturnVelocity) * (CandidateVelocity|CandidateVelocity); //Vectors are rated by their length and their overall forward movement. if (CandidateScore > BestScore) { BestScore = CandidateScore; BestVelocity = CandidateVelocity; } } } ReturnVelocity = BestVelocity; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (DebugMode) { DrawDebugDirectionalArrow(MyWorld, inAvoidanceData.Center + inAvoidanceData.Velocity, inAvoidanceData.Center + (ReturnVelocity / DeltaTime), 75.0f, FColor(64,255,64), true, 2.0f, SDPG_MAX); } #endif } return ReturnVelocity / DeltaTime; //Remove prediction-time scaling }
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; }
FRotator USplineComponent::GetWorldRotationAtTime(float Time, bool bUseConstantVelocity) const { FVector WorldDir = GetWorldDirectionAtTime(Time, bUseConstantVelocity); return WorldDir.Rotation(); }
FRotator USplineComponent::GetWorldRotationAtDistanceAlongSpline(float Distance) const { const FVector Dir = GetWorldDirectionAtDistanceAlongSpline(Distance); return Dir.Rotation(); }
FString UKismetStringLibrary::Conv_VectorToString(FVector InVec) { return InVec.ToString(); }
void ConvertQueryImpactHit(const UWorld* World, const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat) { SCOPE_CYCLE_COUNTER(STAT_ConvertQueryImpactHit); checkSlow(PHit.flags & PxHitFlag::eDISTANCE); const bool bInitialOverlap = PHit.hadInitialOverlap(); if (bInitialOverlap && Geom != nullptr) { ConvertOverlappedShapeToImpactHit(World, PHit, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat); return; } // See if this is a 'blocking' hit const PxFilterData PShapeFilter = PHit.shape->getQueryFilterData(); const PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter); OutResult.bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); OutResult.bStartPenetrating = bInitialOverlap; // calculate the hit time const float HitTime = PHit.distance/CheckLength; OutResult.Time = HitTime; // figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint const FVector TraceStartToEnd = EndLoc - StartLoc; const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceStartToEnd); OutResult.Location = SafeLocationToFitShape; const bool bUsePxPoint = ((PHit.flags & PxHitFlag::ePOSITION) && !bInitialOverlap); OutResult.ImpactPoint = bUsePxPoint ? P2UVector(PHit.position) : StartLoc; // Caution: we may still have an initial overlap, but with null Geom. This is the case for RayCast results. const bool bUsePxNormal = ((PHit.flags & PxHitFlag::eNORMAL) && !bInitialOverlap); FVector Normal = bUsePxNormal ? P2UVector(PHit.normal).GetSafeNormal() : -TraceStartToEnd.GetSafeNormal(); OutResult.Normal = Normal; OutResult.ImpactNormal = Normal; OutResult.TraceStart = StartLoc; OutResult.TraceEnd = EndLoc; #if ENABLE_CHECK_HIT_NORMAL CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom); #endif // ENABLE_CHECK_HIT_NORMAL if (bUsePxNormal && !Normal.IsNormalized()) { // TraceStartToEnd should never be zero, because of the length restriction in the raycast and sweep tests. Normal = -TraceStartToEnd.GetSafeNormal(); OutResult.Normal = Normal; OutResult.ImpactNormal = Normal; } const PxGeometryType::Enum SweptGeometryType = Geom ? Geom->getType() : PxGeometryType::eINVALID; OutResult.ImpactNormal = FindGeomOpposingNormal(SweptGeometryType, PHit, TraceStartToEnd, Normal); // Fill in Actor, Component, material, etc. SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); if( PHit.shape->getGeometryType() == PxGeometryType::eHEIGHTFIELD) { // Lookup physical material for heightfields if (bReturnPhysMat && PHit.faceIndex != InvalidQueryHit.faceIndex) { PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex); if (HitMaterial != NULL) { OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData); } } } else if(bReturnFaceIndex && PHit.shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH) { PxTriangleMeshGeometry PTriMeshGeom; if( PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && PTriMeshGeom.triangleMesh != NULL && PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() ) { OutResult.FaceIndex = PTriMeshGeom.triangleMesh->getTrianglesRemap()[PHit.faceIndex]; } } }
EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass) { SCOPE_CYCLE_COUNTER(STAT_MoveToLocation); EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed; bool bCanRequestMove = true; UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d) Filter(%s)") , TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe, *GetNameSafe(FilterClass)); // Check input is valid if (Dest.ContainsNaN()) { UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)") , TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); ensure(!Dest.ContainsNaN()); bCanRequestMove = false; } FVector GoalLocation = Dest; // fail if projection to navigation is required but it failed if (bCanRequestMove && bProjectDestinationToNavigation) { UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef(); FNavLocation ProjectedLocation; if (NavSys && !NavSys->ProjectPointToNavigation(Dest, ProjectedLocation, AgentProps.GetExtent(), &AgentProps)) { UE_VLOG_LOCATION(this, LogAINavigation, Error, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh")); bCanRequestMove = false; } GoalLocation = ProjectedLocation.Location; } if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap)) { UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: already at goal!")); // make sure previous move request gets aborted PathFollowingComponent->AbortMove(TEXT("Aborting move due to new move request finishing with AlreadyAtGoal"), FAIRequestID::AnyRequest); PathFollowingComponent->SetLastMoveAtGoal(true); OnMoveCompleted(FAIRequestID::CurrentRequest, EPathFollowingResult::Success); Result = EPathFollowingRequestResult::AlreadyAtGoal; bCanRequestMove = false; } if (bCanRequestMove) { FPathFindingQuery Query; const bool bValidQuery = PreparePathfinding(Query, GoalLocation, NULL, bUsePathfinding, FilterClass); const FAIRequestID RequestID = bValidQuery ? RequestPathAndMove(Query, NULL, AcceptanceRadius, bStopOnOverlap, NULL) : FAIRequestID::InvalidRequest; if (RequestID.IsValid()) { bAllowStrafe = bCanStrafe; Result = EPathFollowingRequestResult::RequestSuccessful; } } if (Result == EPathFollowingRequestResult::Failed) { if (PathFollowingComponent) { PathFollowingComponent->SetLastMoveAtGoal(false); } OnMoveCompleted(FAIRequestID::InvalidRequest, EPathFollowingResult::Invalid); } return Result; }
FVector FindBestOverlappingNormal(const UWorld* World, const PxGeometry& Geom, const PxTransform& QueryTM, const GeomType& ShapeGeom, const PxTransform& PShapeWorldPose, PxU32* HitTris, int32 NumTrisHit, bool bCanDrawOverlaps = false) { #if DRAW_OVERLAPPING_TRIS const float Lifetime = 5.f; bCanDrawOverlaps &= World && World->IsGameWorld() && World->PersistentLineBatcher && (World->PersistentLineBatcher->BatchedLines.Num() < 2048); if (bCanDrawOverlaps) { TArray<FOverlapResult> Overlaps; DrawGeomOverlaps(World, Geom, QueryTM, Overlaps, Lifetime); } const FLinearColor LineColor = FLinearColor::Green; const FLinearColor NormalColor = FLinearColor::Red; const FLinearColor PointColor = FLinearColor::Yellow; #endif // DRAW_OVERLAPPING_TRIS // Track the best triangle plane distance float BestPlaneDist = -BIG_NUMBER; FVector BestPlaneNormal(0, 0, 1); // Iterate over triangles for (int32 TriIdx = 0; TriIdx < NumTrisHit; TriIdx++) { PxTriangle Tri; PxMeshQuery::getTriangle(ShapeGeom, PShapeWorldPose, HitTris[TriIdx], Tri); const FVector A = P2UVector(Tri.verts[0]); const FVector B = P2UVector(Tri.verts[1]); const FVector C = P2UVector(Tri.verts[2]); FVector TriNormal = ((B - A) ^ (C - A)); TriNormal = TriNormal.GetSafeNormal(); const FPlane TriPlane(A, TriNormal); const FVector QueryCenter = P2UVector(QueryTM.p); const float DistToPlane = TriPlane.PlaneDot(QueryCenter); if (DistToPlane > BestPlaneDist) { BestPlaneDist = DistToPlane; BestPlaneNormal = TriNormal; } #if DRAW_OVERLAPPING_TRIS if (bCanDrawOverlaps && (World->PersistentLineBatcher->BatchedLines.Num() < 2048)) { static const float LineThickness = 0.9f; static const float NormalThickness = 0.75f; static const float PointThickness = 5.0f; World->PersistentLineBatcher->DrawLine(A, B, LineColor, SDPG_Foreground, LineThickness, Lifetime); World->PersistentLineBatcher->DrawLine(B, C, LineColor, SDPG_Foreground, LineThickness, Lifetime); World->PersistentLineBatcher->DrawLine(C, A, LineColor, SDPG_Foreground, LineThickness, Lifetime); const FVector Centroid((A + B + C) / 3.f); World->PersistentLineBatcher->DrawLine(Centroid, Centroid + (35.0f*TriNormal), NormalColor, SDPG_Foreground, NormalThickness, Lifetime); World->PersistentLineBatcher->DrawPoint(Centroid + (35.0f*TriNormal), NormalColor, PointThickness, SDPG_Foreground, Lifetime); World->PersistentLineBatcher->DrawPoint(A, PointColor, PointThickness, SDPG_Foreground, Lifetime); World->PersistentLineBatcher->DrawPoint(B, PointColor, PointThickness, SDPG_Foreground, Lifetime); World->PersistentLineBatcher->DrawPoint(C, PointColor, PointThickness, SDPG_Foreground, Lifetime); } #endif // DRAW_OVERLAPPING_TRIS } return BestPlaneNormal; }
bool UTB_AimComponent::MissLineTrace(FHitResult &OutHit) { if (!EnemyTarget) { UE_LOG(TB_Log, Error, TEXT("MissLineTrace(): EnemyTarget == NULL")); return false; } //Pick a HitLocation at random TArray<FVector> HitLocations; EnemyTarget->GetHitLocations(HitLocations); UTB_Library::Shuffle(HitLocations); FCollisionQueryParams Params; InitCollisionQueryParams(Params); FCollisionObjectQueryParams ObjectParams; FVector StartLocation = GetComponentLocation(); float Offset = 30; for (auto HitLocation : HitLocations) { bool HitSomething = World->LineTraceSingle(OutHit, StartLocation, HitLocation, Params, ObjectParams); if (HitSomething) { if (EnemyTarget->GetUniqueID() != OutHit.Actor->GetUniqueID()) { //we've hit something we didn't aim for \o/ return true; } else { //we need to adjust the trace so it misses the target // Create a new vector for the trace FVector TraceVector = HitLocation - StartLocation; TraceVector.Normalize(); TraceVector *= Weapon->MaxRange; // Rotate it by 0-5 deg in all directions FRotator Offset(FMath::FRandRange(-10, 10), FMath::FRandRange(-10, 10), 0); TraceVector = Offset.RotateVector(TraceVector); HitSomething = World->LineTraceSingle(OutHit, StartLocation, StartLocation + TraceVector, Params, ObjectParams); if (HitSomething && EnemyTarget->GetUniqueID() == OutHit.Actor->GetUniqueID()) { //we're still hitting the target, pray that we'll miss it when we look at the other targetpoints continue; } else { return HitSomething; } } } } UE_LOG(TB_Log, Warning, TEXT("MissLineTrace(): Can't find a way to miss")); return false; }
void UEnvQueryGenerator_OnCircle::GenerateItemsForCircle(const FVector& CenterLocation, const FVector& StartDirection, int32 StepsCount, float AngleStep, FEnvQueryInstance& OutQueryInstance) const { TArray<FVector> ItemCandidates; ItemCandidates.AddZeroed(StepsCount); for (int32 Step = 0; Step < StepsCount; ++Step) { ItemCandidates[Step] = CenterLocation + StartDirection.RotateAngleAxis(AngleStep*Step, FVector::UpVector); } #if WITH_RECAST // @todo this needs to be optimize to batch raycasts const ARecastNavMesh* NavMesh = (TraceData.TraceMode == EEnvQueryTrace::Navigation) || (ProjectionData.TraceMode == EEnvQueryTrace::Navigation) ? FEQSHelpers::FindNavMeshForQuery(OutQueryInstance) : NULL; if (NavMesh) { NavMesh->BeginBatchQuery(); } #endif if (TraceData.TraceMode == EEnvQueryTrace::Navigation) { #if WITH_RECAST if (NavMesh != NULL) { TSharedPtr<const FNavigationQueryFilter> NavigationFilter = UNavigationQueryFilter::GetQueryFilter(NavMesh, TraceData.NavigationFilter); TArray<FNavigationRaycastWork> RaycastWorkload; RaycastWorkload.Reserve(ItemCandidates.Num()); for (const auto& ItemLocation : ItemCandidates) { RaycastWorkload.Add(FNavigationRaycastWork(CenterLocation, ItemLocation)); } NavMesh->BatchRaycast(RaycastWorkload, NavigationFilter); for (int32 ItemIndex = 0; ItemIndex < ItemCandidates.Num(); ++ItemIndex) { ItemCandidates[ItemIndex] = RaycastWorkload[ItemIndex].HitLocation.Location; } } #endif } else { ECollisionChannel TraceCollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceData.TraceChannel); FVector TraceExtent(TraceData.ExtentX, TraceData.ExtentY, TraceData.ExtentZ); FCollisionQueryParams TraceParams(TEXT("EnvQueryTrace"), TraceData.bTraceComplex); TraceParams.bTraceAsyncScene = true; FBatchTracingHelper TracingHelper(OutQueryInstance.World, TraceCollisionChannel, TraceParams, TraceExtent); switch (TraceData.TraceShape) { case EEnvTraceShape::Line: TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Line>(CenterLocation, ItemCandidates); break; case EEnvTraceShape::Sphere: TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Sphere>(CenterLocation, ItemCandidates); break; case EEnvTraceShape::Capsule: TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Capsule>(CenterLocation, ItemCandidates); break; case EEnvTraceShape::Box: TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Box>(CenterLocation, ItemCandidates); break; default: UE_VLOG(Cast<AActor>(OutQueryInstance.Owner.Get()), LogEQS, Warning, TEXT("UEnvQueryGenerator_OnCircle::CalcDirection failed to calc direction in %s. Using querier facing."), *OutQueryInstance.QueryName); break; } } #if WITH_RECAST if (NavMesh) { ProjectAndFilterNavPoints(ItemCandidates, NavMesh); NavMesh->FinishBatchQuery(); } #endif for (int32 Step = 0; Step < ItemCandidates.Num(); ++Step) { OutQueryInstance.AddItemData<UEnvQueryItemType_Point>(ItemCandidates[Step]); } }
bool AAIController::LineOfSightTo(const AActor* Other, FVector ViewPoint, bool bAlternateChecks) const { if (Other == nullptr) { return false; } if (ViewPoint.IsZero()) { FRotator ViewRotation; GetActorEyesViewPoint(ViewPoint, ViewRotation); // if we still don't have a view point we simply fail if (ViewPoint.IsZero()) { return false; } } static FName NAME_LineOfSight = FName(TEXT("LineOfSight")); FVector TargetLocation = Other->GetTargetLocation(GetPawn()); FCollisionQueryParams CollisionParams(NAME_LineOfSight, true, this->GetPawn()); CollisionParams.AddIgnoredActor(Other); bool bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, TargetLocation, ECC_Visibility, CollisionParams); if (!bHit) { return true; } // if other isn't using a cylinder for collision and isn't a Pawn (which already requires an accurate cylinder for AI) // then don't go any further as it likely will not be tracing to the correct location const APawn * OtherPawn = Cast<const APawn>(Other); if (!OtherPawn && Cast<UCapsuleComponent>(Other->GetRootComponent()) == NULL) { return false; } const FVector OtherActorLocation = Other->GetActorLocation(); const float DistSq = (OtherActorLocation - ViewPoint).SizeSquared(); if (DistSq > FARSIGHTTHRESHOLDSQUARED) { return false; } if (!OtherPawn && (DistSq > NEARSIGHTTHRESHOLDSQUARED)) { return false; } float OtherRadius, OtherHeight; Other->GetSimpleCollisionCylinder(OtherRadius, OtherHeight); if (!bAlternateChecks || !bLOSflag) { //try viewpoint to head bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, OtherActorLocation + FVector(0.f, 0.f, OtherHeight), ECC_Visibility, CollisionParams); if (!bHit) { return true; } } if (!bSkipExtraLOSChecks && (!bAlternateChecks || bLOSflag)) { // only check sides if width of other is significant compared to distance if (OtherRadius * OtherRadius / (OtherActorLocation - ViewPoint).SizeSquared() < 0.0001f) { return false; } //try checking sides - look at dist to four side points, and cull furthest and closest FVector Points[4]; Points[0] = OtherActorLocation - FVector(OtherRadius, -1 * OtherRadius, 0); Points[1] = OtherActorLocation + FVector(OtherRadius, OtherRadius, 0); Points[2] = OtherActorLocation - FVector(OtherRadius, OtherRadius, 0); Points[3] = OtherActorLocation + FVector(OtherRadius, -1 * OtherRadius, 0); int32 IndexMin = 0; int32 IndexMax = 0; float CurrentMax = (Points[0] - ViewPoint).SizeSquared(); float CurrentMin = CurrentMax; for (int32 PointIndex = 1; PointIndex<4; PointIndex++) { const float NextSize = (Points[PointIndex] - ViewPoint).SizeSquared(); if (NextSize > CurrentMin) { CurrentMin = NextSize; IndexMax = PointIndex; } else if (NextSize < CurrentMax) { CurrentMax = NextSize; IndexMin = PointIndex; } } for (int32 PointIndex = 0; PointIndex<4; PointIndex++) { if ((PointIndex != IndexMin) && (PointIndex != IndexMax)) { bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, Points[PointIndex], ECC_Visibility, CollisionParams); if (!bHit) { return true; } } } } return false; }
void FAnimNode_TwoBoneIK::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); // Get indices of the lower and upper limb bones and check validity. bool bInvalidLimb = false; FCompactPoseBoneIndex IKBoneCompactPoseIndex = IKBone.GetCompactPoseIndex(BoneContainer); const FCompactPoseBoneIndex LowerLimbIndex = BoneContainer.GetParentBoneIndex(IKBoneCompactPoseIndex); if (LowerLimbIndex == INDEX_NONE) { bInvalidLimb = true; } const FCompactPoseBoneIndex UpperLimbIndex = BoneContainer.GetParentBoneIndex(LowerLimbIndex); if (UpperLimbIndex == INDEX_NONE) { bInvalidLimb = true; } const bool bInBoneSpace = (EffectorLocationSpace == BCS_ParentBoneSpace) || (EffectorLocationSpace == BCS_BoneSpace); const int32 EffectorBoneIndex = bInBoneSpace ? BoneContainer.GetPoseBoneIndexForBoneName(EffectorSpaceBoneName) : INDEX_NONE; const FCompactPoseBoneIndex EffectorSpaceBoneIndex = BoneContainer.MakeCompactPoseIndex(FMeshPoseBoneIndex(EffectorBoneIndex)); if (bInBoneSpace && (EffectorSpaceBoneIndex == INDEX_NONE)) { bInvalidLimb = true; } // If we walked past the root, this controlled is invalid, so return no affected bones. if( bInvalidLimb ) { return; } // Get Local Space transforms for our bones. We do this first in case they already are local. // As right after we get them in component space. (And that does the auto conversion). // We might save one transform by doing local first... const FTransform EndBoneLocalTransform = MeshBases.GetLocalSpaceTransform(IKBoneCompactPoseIndex); // Now get those in component space... FTransform LowerLimbCSTransform = MeshBases.GetComponentSpaceTransform(LowerLimbIndex); FTransform UpperLimbCSTransform = MeshBases.GetComponentSpaceTransform(UpperLimbIndex); FTransform EndBoneCSTransform = MeshBases.GetComponentSpaceTransform(IKBoneCompactPoseIndex); // Get current position of root of limb. // All position are in Component space. const FVector RootPos = UpperLimbCSTransform.GetTranslation(); const FVector InitialJointPos = LowerLimbCSTransform.GetTranslation(); const FVector InitialEndPos = EndBoneCSTransform.GetTranslation(); // Transform EffectorLocation from EffectorLocationSpace to ComponentSpace. FTransform EffectorTransform(EffectorLocation); FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, EffectorTransform, EffectorSpaceBoneIndex, EffectorLocationSpace); // This is our reach goal. FVector DesiredPos = EffectorTransform.GetTranslation(); FVector DesiredDelta = DesiredPos - RootPos; float DesiredLength = DesiredDelta.Size(); // Check to handle case where DesiredPos is the same as RootPos. FVector DesiredDir; if (DesiredLength < (float)KINDA_SMALL_NUMBER) { DesiredLength = (float)KINDA_SMALL_NUMBER; DesiredDir = FVector(1,0,0); } else { DesiredDir = DesiredDelta / DesiredLength; } // Get joint target (used for defining plane that joint should be in). FTransform JointTargetTransform(JointTargetLocation); FCompactPoseBoneIndex JointTargetSpaceBoneIndex(INDEX_NONE); if (JointTargetLocationSpace == BCS_ParentBoneSpace || JointTargetLocationSpace == BCS_BoneSpace) { int32 Index = BoneContainer.GetPoseBoneIndexForBoneName(JointTargetSpaceBoneName); JointTargetSpaceBoneIndex = BoneContainer.MakeCompactPoseIndex(FMeshPoseBoneIndex(Index)); } FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, JointTargetTransform, JointTargetSpaceBoneIndex, JointTargetLocationSpace); FVector JointTargetPos = JointTargetTransform.GetTranslation(); FVector JointTargetDelta = JointTargetPos - RootPos; float JointTargetLength = JointTargetDelta.Size(); // Same check as above, to cover case when JointTarget position is the same as RootPos. FVector JointPlaneNormal, JointBendDir; if (JointTargetLength < (float)KINDA_SMALL_NUMBER) { JointBendDir = FVector(0,1,0); JointPlaneNormal = FVector(0,0,1); } else { JointPlaneNormal = DesiredDir ^ JointTargetDelta; // If we are trying to point the limb in the same direction that we are supposed to displace the joint in, // we have to just pick 2 random vector perp to DesiredDir and each other. if (JointPlaneNormal.Size() < (float)KINDA_SMALL_NUMBER) { DesiredDir.FindBestAxisVectors(JointPlaneNormal, JointBendDir); } else { JointPlaneNormal.Normalize(); // Find the final member of the reference frame by removing any component of JointTargetDelta along DesiredDir. // This should never leave a zero vector, because we've checked DesiredDir and JointTargetDelta are not parallel. JointBendDir = JointTargetDelta - ((JointTargetDelta | DesiredDir) * DesiredDir); JointBendDir.Normalize(); } } // Find lengths of upper and lower limb in the ref skeleton. // Use actual sizes instead of ref skeleton, so we take into account translation and scaling from other bone controllers. float LowerLimbLength = (InitialEndPos - InitialJointPos).Size(); float UpperLimbLength = (InitialJointPos - RootPos).Size(); float MaxLimbLength = LowerLimbLength + UpperLimbLength; if (bAllowStretching) { const float ScaleRange = StretchLimits.Y - StretchLimits.X; if( ScaleRange > KINDA_SMALL_NUMBER && MaxLimbLength > KINDA_SMALL_NUMBER ) { const float ReachRatio = DesiredLength / MaxLimbLength; const float ScalingFactor = (StretchLimits.Y - 1.f) * FMath::Clamp<float>((ReachRatio - StretchLimits.X) / ScaleRange, 0.f, 1.f); if (ScalingFactor > KINDA_SMALL_NUMBER) { LowerLimbLength *= (1.f + ScalingFactor); UpperLimbLength *= (1.f + ScalingFactor); MaxLimbLength *= (1.f + ScalingFactor); } } } FVector OutEndPos = DesiredPos; FVector OutJointPos = InitialJointPos; // If we are trying to reach a goal beyond the length of the limb, clamp it to something solvable and extend limb fully. if (DesiredLength > MaxLimbLength) { OutEndPos = RootPos + (MaxLimbLength * DesiredDir); OutJointPos = RootPos + (UpperLimbLength * DesiredDir); } else { // So we have a triangle we know the side lengths of. We can work out the angle between DesiredDir and the direction of the upper limb // using the sin rule: const float TwoAB = 2.f * UpperLimbLength * DesiredLength; const float CosAngle = (TwoAB != 0.f) ? ((UpperLimbLength*UpperLimbLength) + (DesiredLength*DesiredLength) - (LowerLimbLength*LowerLimbLength)) / TwoAB : 0.f; // If CosAngle is less than 0, the upper arm actually points the opposite way to DesiredDir, so we handle that. const bool bReverseUpperBone = (CosAngle < 0.f); // If CosAngle is greater than 1.f, the triangle could not be made - we cannot reach the target. // We just have the two limbs double back on themselves, and EndPos will not equal the desired EffectorLocation. if ((CosAngle > 1.f) || (CosAngle < -1.f)) { // Because we want the effector to be a positive distance down DesiredDir, we go back by the smaller section. if (UpperLimbLength > LowerLimbLength) { OutJointPos = RootPos + (UpperLimbLength * DesiredDir); OutEndPos = OutJointPos - (LowerLimbLength * DesiredDir); } else { OutJointPos = RootPos - (UpperLimbLength * DesiredDir); OutEndPos = OutJointPos + (LowerLimbLength * DesiredDir); } } else { // Angle between upper limb and DesiredDir const float Angle = FMath::Acos(CosAngle); // Now we calculate the distance of the joint from the root -> effector line. // This forms a right-angle triangle, with the upper limb as the hypotenuse. const float JointLineDist = UpperLimbLength * FMath::Sin(Angle); // And the final side of that triangle - distance along DesiredDir of perpendicular. // ProjJointDistSqr can't be neg, because JointLineDist must be <= UpperLimbLength because appSin(Angle) is <= 1. const float ProjJointDistSqr = (UpperLimbLength*UpperLimbLength) - (JointLineDist*JointLineDist); // although this shouldn't be ever negative, sometimes Xbox release produces -0.f, causing ProjJointDist to be NaN // so now I branch it. float ProjJointDist = (ProjJointDistSqr>0.f)? FMath::Sqrt(ProjJointDistSqr) : 0.f; if( bReverseUpperBone ) { ProjJointDist *= -1.f; } // So now we can work out where to put the joint! OutJointPos = RootPos + (ProjJointDist * DesiredDir) + (JointLineDist * JointBendDir); } } // Update transform for upper bone. { // Get difference in direction for old and new joint orientations FVector const OldDir = (InitialJointPos - RootPos).GetSafeNormal(); FVector const NewDir = (OutJointPos - RootPos).GetSafeNormal(); // Find Delta Rotation take takes us from Old to New dir FQuat const DeltaRotation = FQuat::FindBetweenNormals(OldDir, NewDir); // Rotate our Joint quaternion by this delta rotation UpperLimbCSTransform.SetRotation( DeltaRotation * UpperLimbCSTransform.GetRotation() ); // And put joint where it should be. UpperLimbCSTransform.SetTranslation( RootPos ); // Order important. First bone is upper limb. OutBoneTransforms.Add( FBoneTransform(UpperLimbIndex, UpperLimbCSTransform) ); } // Update transform for lower bone. { // Get difference in direction for old and new joint orientations FVector const OldDir = (InitialEndPos - InitialJointPos).GetSafeNormal(); FVector const NewDir = (OutEndPos - OutJointPos).GetSafeNormal(); // Find Delta Rotation take takes us from Old to New dir FQuat const DeltaRotation = FQuat::FindBetweenNormals(OldDir, NewDir); // Rotate our Joint quaternion by this delta rotation LowerLimbCSTransform.SetRotation( DeltaRotation * LowerLimbCSTransform.GetRotation() ); // And put joint where it should be. LowerLimbCSTransform.SetTranslation( OutJointPos ); // Order important. Second bone is lower limb. OutBoneTransforms.Add( FBoneTransform(LowerLimbIndex, LowerLimbCSTransform) ); } // Update transform for end bone. { if( bTakeRotationFromEffectorSpace ) { EndBoneCSTransform.SetRotation( EffectorTransform.GetRotation() ); } else if( bMaintainEffectorRelRot ) { EndBoneCSTransform = EndBoneLocalTransform * LowerLimbCSTransform; } // Set correct location for end bone. EndBoneCSTransform.SetTranslation(OutEndPos); // Order important. Third bone is End Bone. OutBoneTransforms.Add(FBoneTransform(IKBoneCompactPoseIndex, EndBoneCSTransform)); } // Make sure we have correct number of bones check(OutBoneTransforms.Num() == 3); }
static double logSum(const FVector &v) { return logSum(v, v.size()); }
void UVaOceanBuoyancyComponent::PerformWaveReaction(float DeltaTime) { AActor* MyOwner = GetOwner(); if (!UpdatedComponent || MyOwner == NULL) { return; } UPrimitiveComponent* OldPrimitive = Cast<UPrimitiveComponent>(UpdatedComponent); const FVector OldLocation = MyOwner->GetActorLocation(); const FRotator OldRotation = MyOwner->GetActorRotation(); const FVector OldLinearVelocity = OldPrimitive->GetPhysicsLinearVelocity(); const FVector OldAngularVelocity = OldPrimitive->GetPhysicsAngularVelocity(); const FVector OldCenterOfMassWorld = OldLocation + OldRotation.RotateVector(COMOffset); const FVector OwnerScale = MyOwner->GetActorScale(); // XYZ === Throttle, Steering, Rise == Forwards, Sidewards, Upwards FVector X, Y, Z; GetAxes(OldRotation, X, Y, Z); // Process tension dots and get torque from wind/waves for (FVector TensionDot : TensionDots) { // Translate point to world coordinates FVector TensionDotDisplaced = OldRotation.RotateVector(TensionDot + COMOffset); FVector TensionDotWorld = OldLocation + TensionDotDisplaced; // Get point depth float DotAltitude = GetAltitude(TensionDotWorld); // Don't process dots above water if (DotAltitude > 0) { continue; } // Surface normal (not modified!) FVector DotSurfaceNormal = GetSurfaceNormal(TensionDotWorld) * GetSurfaceWavesNum(); // Modify normal with real Z value and normalize it DotSurfaceNormal.Z = GetOceanLevel(TensionDotWorld); DotSurfaceNormal.Normalize(); // Point dynamic pressure [http://en.wikipedia.org/wiki/Dynamic_pressure] // rho = 1.03f for ocean water FVector WaveVelocity = GetWaveVelocity(TensionDotWorld); float DotQ = 0.515f * FMath::Square(WaveVelocity.Size()); FVector WaveForce = FVector(0.0,0.0,1.0) * DotQ /* DotSurfaceNormal*/ * (-DotAltitude) * TensionDepthFactor; // We don't want Z to be affected by DotQ WaveForce.Z /= DotQ; // Scale to DeltaTime to break FPS addiction WaveForce *= DeltaTime; // Apply actor scale WaveForce *= OwnerScale.X;// *OwnerScale.Y * OwnerScale.Z; OldPrimitive->AddForceAtLocation(WaveForce * Mass, TensionDotWorld); } // Static metacentric forces (can be useful on small waves) if (bUseMetacentricForces) { FVector TensionTorqueResult = FVector(0.0f, 0.0f, 0.0f); // Calc recovering torque (transverce) FRotator RollRot = FRotator(0.0f, 0.0f, 0.0f); RollRot.Roll = OldRotation.Roll; FVector MetacenterDisplaced = RollRot.RotateVector(TransverseMetacenter + COMOffset); TensionTorqueResult += X * FVector::DotProduct((TransverseMetacenter - MetacenterDisplaced), FVector(0.0f, -1.0f, 0.0f)) * TensionTorqueRollFactor; // Calc recovering torque (longitude) FRotator PitchRot = FRotator(0.0f, 0.0f, 0.0f); PitchRot.Pitch = OldRotation.Pitch; MetacenterDisplaced = PitchRot.RotateVector(LongitudinalMetacenter + COMOffset); TensionTorqueResult += Y * FVector::DotProduct((LongitudinalMetacenter - MetacenterDisplaced), FVector(1.0f, 0.0f, 0.0f)) * TensionTorquePitchFactor; // Apply torque TensionTorqueResult *= DeltaTime; TensionTorqueResult *= OwnerScale.X;// *OwnerScale.Y * OwnerScale.Z; OldPrimitive->AddTorque(TensionTorqueResult); } }
FVector UTKMathFunctionLibrary::ClosestPointOnSphereToLine(FVector SphereOrigin, float SphereRadius, FVector LineOrigin, FVector LineDir) { static FVector OutClosestPoint; FMath::SphereDistToLine(SphereOrigin, SphereRadius, LineOrigin, LineDir.GetSafeNormal(), OutClosestPoint); return OutClosestPoint; }
/** * Utility that ensures the verts supplied form a valid hull. Will modify the verts to remove any duplicates. * Positions should be in physics scale. * Returns true is hull is valid. */ static bool EnsureHullIsValid(TArray<FVector>& InVerts) { RemoveDuplicateVerts(InVerts); if(InVerts.Num() < 3) { return false; } // Take any vert. In this case - the first one. const FVector FirstVert = InVerts[0]; // Now find vert furthest from this one. float FurthestDistSqr = 0.f; int32 FurthestVertIndex = INDEX_NONE; for(int32 i=1; i<InVerts.Num(); i++) { const float TestDistSqr = (InVerts[i] - FirstVert).SizeSquared(); if(TestDistSqr > FurthestDistSqr) { FurthestDistSqr = TestDistSqr; FurthestVertIndex = i; } } // If smallest dimension is too small - hull is invalid. if(FurthestVertIndex == INDEX_NONE || FurthestDistSqr < FMath::Square(MIN_HULL_VALID_DIMENSION)) { return false; } // Now find point furthest from line defined by these 2 points. float ThirdPointDist = 0.f; int32 ThirdPointIndex = INDEX_NONE; for(int32 i=1; i<InVerts.Num(); i++) { if(i != FurthestVertIndex) { const float TestDist = DistanceToLine(FirstVert, InVerts[FurthestVertIndex], InVerts[i]); if(TestDist > ThirdPointDist) { ThirdPointDist = TestDist; ThirdPointIndex = i; } } } // If this dimension is too small - hull is invalid. if(ThirdPointIndex == INDEX_NONE || ThirdPointDist < MIN_HULL_VALID_DIMENSION) { return false; } // Now we check each remaining point against this plane. // First find plane normal. const FVector Dir1 = InVerts[FurthestVertIndex] - InVerts[0]; const FVector Dir2 = InVerts[ThirdPointIndex] - InVerts[0]; FVector PlaneNormal = Dir1 ^ Dir2; const bool bNormalizedOk = PlaneNormal.Normalize(); if(!bNormalizedOk) { return false; } // Now iterate over all remaining vertices. float MaxThickness = 0.f; for(int32 i=1; i<InVerts.Num(); i++) { if((i != FurthestVertIndex) && (i != ThirdPointIndex)) { const float PointPlaneDist = FMath::Abs((InVerts[i] - InVerts[0]) | PlaneNormal); MaxThickness = FMath::Max(PointPlaneDist, MaxThickness); } } if(MaxThickness < MIN_HULL_VALID_DIMENSION) { return false; } return true; }
FVector UTKMathFunctionLibrary::SetVectorLength(FVector A, float size) { return A.GetSafeNormal() * size; }
bool FKConvexElem::HullFromPlanes(const TArray<FPlane>& InPlanes, const TArray<FVector>& SnapVerts) { // Start by clearing this convex. Reset(); float TotalPolyArea = 0; for(int32 i=0; i<InPlanes.Num(); i++) { FPoly Polygon; Polygon.Normal = InPlanes[i]; FVector AxisX, AxisY; Polygon.Normal.FindBestAxisVectors(AxisX,AxisY); const FVector Base = InPlanes[i] * InPlanes[i].W; new(Polygon.Vertices) FVector(Base + AxisX * HALF_WORLD_MAX + AxisY * HALF_WORLD_MAX); new(Polygon.Vertices) FVector(Base - AxisX * HALF_WORLD_MAX + AxisY * HALF_WORLD_MAX); new(Polygon.Vertices) FVector(Base - AxisX * HALF_WORLD_MAX - AxisY * HALF_WORLD_MAX); new(Polygon.Vertices) FVector(Base + AxisX * HALF_WORLD_MAX - AxisY * HALF_WORLD_MAX); for(int32 j=0; j<InPlanes.Num(); j++) { if(i != j) { if(!Polygon.Split(-FVector(InPlanes[j]), InPlanes[j] * InPlanes[j].W)) { Polygon.Vertices.Empty(); break; } } } // Do nothing if poly was completely clipped away. if(Polygon.Vertices.Num() > 0) { TotalPolyArea += Polygon.Area(); // Add vertices of polygon to convex primitive. for(int32 j=0; j<Polygon.Vertices.Num(); j++) { // We try and snap the vert to on of the ones supplied. int32 NearestVert = INDEX_NONE; float NearestDistSqr = BIG_NUMBER; for(int32 k=0; k<SnapVerts.Num(); k++) { const float DistSquared = (Polygon.Vertices[j] - SnapVerts[k]).SizeSquared(); if( DistSquared < NearestDistSqr ) { NearestVert = k; NearestDistSqr = DistSquared; } } // If we have found a suitably close vertex, use that if( NearestVert != INDEX_NONE && NearestDistSqr < LOCAL_EPS ) { const FVector localVert = SnapVerts[NearestVert]; AddVertexIfNotPresent(VertexData, localVert); } else { const FVector localVert = Polygon.Vertices[j]; AddVertexIfNotPresent(VertexData, localVert); } } } } // If the collision volume isn't closed, return an error so the model can be discarded if(TotalPolyArea < 0.001f) { UE_LOG(LogPhysics, Log, TEXT("Total Polygon Area invalid: %f"), TotalPolyArea ); return false; } // We need at least 4 vertices to make a convex hull with non-zero volume. // We shouldn't have the same vertex multiple times (using AddVertexIfNotPresent above) if(VertexData.Num() < 4) { return true; } // Check that not all vertices lie on a line (ie. find plane) // Again, this should be a non-zero vector because we shouldn't have duplicate verts. bool bFound = false; FVector Dir2, Dir1; Dir1 = VertexData[1] - VertexData[0]; Dir1.Normalize(); for(int32 i=2; i<VertexData.Num() && !bFound; i++) { Dir2 = VertexData[i] - VertexData[0]; Dir2.Normalize(); // If line are non-parallel, this vertex forms our plane if((Dir1 | Dir2) < (1.f - LOCAL_EPS)) { bFound = true; } } if(!bFound) { return true; } // Now we check that not all vertices lie on a plane, by checking at least one lies off the plane we have formed. FVector Normal = Dir1 ^ Dir2; Normal.Normalize(); const FPlane Plane(VertexData[0], Normal); bFound = false; for(int32 i=2; i<VertexData.Num() ; i++) { if(Plane.PlaneDot(VertexData[i]) > LOCAL_EPS) { bFound = true; break; } } // If we did not find a vert off the line - discard this hull. if(!bFound) { return true; } // calc bounding box of verts UpdateElemBox(); // We can continue adding primitives (mesh is not horribly broken) return true; }
/** Check if this vertex is in the same place as given point */ FORCEINLINE bool IsSame( const FVector &v ) { const float eps = 0.01f; return v.Equals( Position, eps ); }
/** * Quantizes floating point light samples down to byte samples with a scale applied to all samples * * @param InLightSamples Floating point light sample coefficients * @param OutLightSamples Quantized light sample coefficients * @param OutScale Scale applied to each quantized sample (to get it back near original floating point value) * @param bUseMappedFlag Whether or not to pay attention to the bIsMapped flag for each sample when calculating max scale * * TODO Calculate residual after compression, not just quantization. * TODO Factor out error from directionality compression and push it to color. This requires knowing a representative normal. * Best way is probably to create a new texture compression type and do error correcting during compression. */ void QuantizeLightSamples( TArray<FLightSample>& InLightSamples, TArray<FQuantizedLightSampleData>& OutLightSamples, float OutMultiply[LM_NUM_STORED_LIGHTMAP_COEF][4], float OutAdd[LM_NUM_STORED_LIGHTMAP_COEF][4], int32 DebugSampleIndex, bool bUseMappedFlag) { //const float EncodeExponent = .5f; const float LogScale = 11.5f; const float LogBlackPoint = FMath::Pow( 2.0f, -0.5f * LogScale ); const float SimpleLogScale = 16.0f; const float SimpleLogBlackPoint = FMath::Pow( 2.0f, -0.5f * SimpleLogScale ); float MinCoefficient[LM_NUM_STORED_LIGHTMAP_COEF][4]; float MaxCoefficient[LM_NUM_STORED_LIGHTMAP_COEF][4]; for( int32 CoefficientIndex = 0; CoefficientIndex < LM_NUM_STORED_LIGHTMAP_COEF; CoefficientIndex += 2 ) { for( int32 ColorIndex = 0; ColorIndex < 4; ColorIndex++ ) { // Color MinCoefficient[ CoefficientIndex ][ ColorIndex ] = 10000.0f; MaxCoefficient[ CoefficientIndex ][ ColorIndex ] = -10000.0f; // Direction MinCoefficient[ CoefficientIndex + 1 ][ ColorIndex ] = 10000.0f; MaxCoefficient[ CoefficientIndex + 1 ][ ColorIndex ] = -10000.0f; } } // go over all samples looking for min and max values for (int32 SampleIndex = 0; SampleIndex < InLightSamples.Num(); SampleIndex++) { const FLightSample& SourceSample = InLightSamples[SampleIndex]; #if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING if (SampleIndex == DebugSampleIndex) { int32 TempBreak = 0; } #endif if(SourceSample.bIsMapped) { { // Complex float L, U, V, W; GetLUVW( SourceSample.Coefficients[0], L, U, V, W ); float LogL = FMath::Log2( L + LogBlackPoint ); MinCoefficient[0][0] = FMath::Min( MinCoefficient[0][0], U ); MaxCoefficient[0][0] = FMath::Max( MaxCoefficient[0][0], U ); MinCoefficient[0][1] = FMath::Min( MinCoefficient[0][1], V ); MaxCoefficient[0][1] = FMath::Max( MaxCoefficient[0][1], V ); MinCoefficient[0][2] = FMath::Min( MinCoefficient[0][2], W ); MaxCoefficient[0][2] = FMath::Max( MaxCoefficient[0][2], W ); MinCoefficient[0][3] = FMath::Min( MinCoefficient[0][3], LogL ); MaxCoefficient[0][3] = FMath::Max( MaxCoefficient[0][3], LogL ); // Dampen dark texel's contribution on the directionality min and max float DampenDirectionality = FMath::Clamp(L * 100, 0.0f, 1.0f); for( int32 ColorIndex = 0; ColorIndex < 3; ColorIndex++ ) { MinCoefficient[1][ColorIndex] = FMath::Min( MinCoefficient[1][ColorIndex], DampenDirectionality * SourceSample.Coefficients[1][ColorIndex] ); MaxCoefficient[1][ColorIndex] = FMath::Max( MaxCoefficient[1][ColorIndex], DampenDirectionality * SourceSample.Coefficients[1][ColorIndex] ); } } { // Simple float L, U, V, W; GetLUVW( SourceSample.Coefficients[2], L, U, V, W ); float LogL = FMath::Log2( L + SimpleLogBlackPoint ) / SimpleLogScale + 0.5f; float LogR = LogL * U; float LogG = LogL * V; float LogB = LogL * W; MinCoefficient[2][0] = FMath::Min( MinCoefficient[2][0], LogR ); MaxCoefficient[2][0] = FMath::Max( MaxCoefficient[2][0], LogR ); MinCoefficient[2][1] = FMath::Min( MinCoefficient[2][1], LogG ); MaxCoefficient[2][1] = FMath::Max( MaxCoefficient[2][1], LogG ); MinCoefficient[2][2] = FMath::Min( MinCoefficient[2][2], LogB ); MaxCoefficient[2][2] = FMath::Max( MaxCoefficient[2][2], LogB ); // Dampen dark texel's contribution on the directionality min and max float DampenDirectionality = FMath::Clamp(L * 100, 0.0f, 1.0f); for( int32 ColorIndex = 0; ColorIndex < 3; ColorIndex++ ) { MinCoefficient[3][ColorIndex] = FMath::Min( MinCoefficient[3][ColorIndex], DampenDirectionality * SourceSample.Coefficients[3][ColorIndex] ); MaxCoefficient[3][ColorIndex] = FMath::Max( MaxCoefficient[3][ColorIndex], DampenDirectionality * SourceSample.Coefficients[3][ColorIndex] ); } } } } // If no sample mapped make range sane. // Or if very dark no directionality is added. Make range sane. for (int32 CoefficientIndex = 0; CoefficientIndex < LM_NUM_STORED_LIGHTMAP_COEF; CoefficientIndex++) { for( int32 ColorIndex = 0; ColorIndex < 4; ColorIndex++ ) { if( MinCoefficient[CoefficientIndex][ColorIndex] > MaxCoefficient[CoefficientIndex][ColorIndex] ) { MinCoefficient[CoefficientIndex][ColorIndex] = 0.0f; MaxCoefficient[CoefficientIndex][ColorIndex] = 0.0f; } } } // Calculate the scale/bias for the light-map coefficients. float CoefficientMultiply[LM_NUM_STORED_LIGHTMAP_COEF][4]; float CoefficientAdd[LM_NUM_STORED_LIGHTMAP_COEF][4]; for (int32 CoefficientIndex = 0; CoefficientIndex < LM_NUM_STORED_LIGHTMAP_COEF; CoefficientIndex++) { for (int32 ColorIndex = 0; ColorIndex < 4; ColorIndex++) { // Calculate scale and bias factors to pack into the desired range // y = (x - Min) / (Max - Min) // Mul = 1 / (Max - Min) // Add = -Min / (Max - Min) CoefficientMultiply[CoefficientIndex][ColorIndex] = 1.0f / FMath::Max<float>(MaxCoefficient[CoefficientIndex][ColorIndex] - MinCoefficient[CoefficientIndex][ColorIndex], DELTA); CoefficientAdd[CoefficientIndex][ColorIndex] = -MinCoefficient[CoefficientIndex][ColorIndex] / FMath::Max<float>(MaxCoefficient[CoefficientIndex][ColorIndex] - MinCoefficient[CoefficientIndex][ColorIndex], DELTA); // Output the values used to undo this packing OutMultiply[CoefficientIndex][ColorIndex] = 1.0f / CoefficientMultiply[CoefficientIndex][ColorIndex]; OutAdd[CoefficientIndex][ColorIndex] = -CoefficientAdd[CoefficientIndex][ColorIndex] / CoefficientMultiply[CoefficientIndex][ColorIndex]; } } // Bias to avoid divide by zero in shader for (int32 ColorIndex = 0; ColorIndex < 3; ColorIndex++) { OutAdd[2][ColorIndex] = FMath::Max( OutAdd[2][ColorIndex], 1e-2f ); } // Force SH constant term to 0.282095f. Avoids add in shader. OutMultiply[1][3] = 0.0f; OutAdd[1][3] = 0.282095f; OutMultiply[3][3] = 0.0f; OutAdd[3][3] = 0.282095f; // allocate space in the output OutLightSamples.Empty(InLightSamples.Num()); OutLightSamples.AddUninitialized(InLightSamples.Num()); // quantize each sample using the above scaling for (int32 SampleIndex = 0; SampleIndex < InLightSamples.Num(); SampleIndex++) { const FLightSample& SourceSample = InLightSamples[SampleIndex]; FQuantizedLightSampleData& DestCoefficients = OutLightSamples[SampleIndex]; #if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING if (SampleIndex == DebugSampleIndex) { int32 TempBreak = 0; } #endif DestCoefficients.Coverage = SourceSample.bIsMapped ? 255 : 0; const FVector BentNormal(SourceSample.SkyOcclusion[0], SourceSample.SkyOcclusion[1], SourceSample.SkyOcclusion[2]); const float BentNormalLength = BentNormal.Size(); const FVector NormalizedBentNormal = BentNormal.GetSafeNormal() * FVector(.5f) + FVector(.5f); DestCoefficients.SkyOcclusion[0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( NormalizedBentNormal[0] * 255.0f ), 0, 255 ); DestCoefficients.SkyOcclusion[1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( NormalizedBentNormal[1] * 255.0f ), 0, 255 ); DestCoefficients.SkyOcclusion[2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( NormalizedBentNormal[2] * 255.0f ), 0, 255 ); // Sqrt on length to allocate more precision near 0 DestCoefficients.SkyOcclusion[3] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Sqrt(BentNormalLength) * 255.0f ), 0, 255 ); // Sqrt to allocate more precision near 0 DestCoefficients.AOMaterialMask = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Sqrt(SourceSample.AOMaterialMask) * 255.0f ), 0, 255 ); { float L, U, V, W; GetLUVW( SourceSample.Coefficients[0], L, U, V, W ); // LogLUVW encode color float LogL = FMath::Log2( L + LogBlackPoint ); U = U * CoefficientMultiply[0][0] + CoefficientAdd[0][0]; V = V * CoefficientMultiply[0][1] + CoefficientAdd[0][1]; W = W * CoefficientMultiply[0][2] + CoefficientAdd[0][2]; LogL = LogL * CoefficientMultiply[0][3] + CoefficientAdd[0][3]; float Residual = LogL * 255.0f - FMath::RoundToFloat( LogL * 255.0f ) + 0.5f; // U, V, W, LogL // UVW stored in gamma space DestCoefficients.Coefficients[0][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Pow( U, 1.0f / 2.2f ) * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[0][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Pow( V, 1.0f / 2.2f ) * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[0][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Pow( W, 1.0f / 2.2f ) * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[0][3] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogL * 255.0f ), 0, 255 ); float Dx = SourceSample.Coefficients[1][0] * CoefficientMultiply[1][0] + CoefficientAdd[1][0]; float Dy = SourceSample.Coefficients[1][1] * CoefficientMultiply[1][1] + CoefficientAdd[1][1]; float Dz = SourceSample.Coefficients[1][2] * CoefficientMultiply[1][2] + CoefficientAdd[1][2]; // Dx, Dy, Dz, Residual DestCoefficients.Coefficients[1][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dx * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[1][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dy * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[1][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dz * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[1][3] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Residual * 255.0f ), 0, 255 ); } { float L, U, V, W; GetLUVW( SourceSample.Coefficients[2], L, U, V, W ); // LogRGB encode color float LogL = FMath::Log2( L + SimpleLogBlackPoint ) / SimpleLogScale + 0.5f; float LogR = LogL * U * CoefficientMultiply[2][0] + CoefficientAdd[2][0]; float LogG = LogL * V * CoefficientMultiply[2][1] + CoefficientAdd[2][1]; float LogB = LogL * W * CoefficientMultiply[2][2] + CoefficientAdd[2][2]; // LogR, LogG, LogB DestCoefficients.Coefficients[2][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogR * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[2][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogG * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[2][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogB * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[2][3] = 255; float Dx = SourceSample.Coefficients[3][0] * CoefficientMultiply[3][0] + CoefficientAdd[3][0]; float Dy = SourceSample.Coefficients[3][1] * CoefficientMultiply[3][1] + CoefficientAdd[3][1]; float Dz = SourceSample.Coefficients[3][2] * CoefficientMultiply[3][2] + CoefficientAdd[3][2]; // Dx, Dy, Dz DestCoefficients.Coefficients[3][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dx * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[3][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dy * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[3][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dz * 255.0f ), 0, 255 ); DestCoefficients.Coefficients[3][3] = 255; } } InLightSamples.Empty(); }