Removed old files, port of both solutions to c++ with fix for return values over my previous upload.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "IntersectionUtils.generated.h"
class UWorld;
/**
*
*/
UCLASS()
class PEM_V1_API UIntersectionUtils : public UBlueprintFunctionLibrary {
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Utils|Intersection")
static bool GetIntersectionFromBoxCenterToEdge(const FVector& InPointToTest, const FVector& InBoxExtent3D, const FTransform& InBoxTransform, FVector& OutIntersectionPoint, const UObject* const InDebugWorldContext = nullptr);
UFUNCTION(BlueprintCallable, Category = "Utils|Intersection")
static bool GetIntersectionFrom2DPlaneCenterToEdge(const FVector2D& InPointToTest, const FVector2D& InPlaneLocation, const FVector2D& InPlaneExtent, float InPlaneYaw, FVector2D& OutIntersectionPoint, const UObject* const InDebugWorldContext = nullptr);
};
#include "IntersectionUtils.h"
#include "Kismet/KismetMathLibrary.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"
bool UIntersectionUtils::GetIntersectionFromBoxCenterToEdge(const FVector& InPointToTest, const FVector& InBoxExtent3D, const FTransform& InBoxTransform, FVector& OutIntersectionPoint, const UObject* const InDebugWorldContext) {
const FVector CenterPoint = InBoxTransform.GetLocation();
const UWorld* InDebugWorld = IsValid(InDebugWorldContext) ? InDebugWorldContext->GetWorld() : nullptr;
const FVector TransformedPointToTest = InBoxTransform.Inverse().TransformVector(InPointToTest - CenterPoint) + CenterPoint;
const bool bInBox = UKismetMathLibrary::IsPointInBox(TransformedPointToTest, CenterPoint, InBoxExtent3D);
const bool bPersistDebug = false;
const float DebugLifetime = -1.f;
if (!bInBox) {
FVector HitLocation = FVector::ZeroVector;;
// FBox do not rotate so we rotate the CenterPoint.
const FVector BoxSweepExtent = FVector::ZeroVector;
const FBox Box = FBox::BuildAABB(CenterPoint, InBoxExtent3D);
FVector HitNormal;
float HitTime;
// Get edge intersection.
FMath::LineExtentBoxIntersection(Box, TransformedPointToTest, CenterPoint, BoxSweepExtent, HitLocation, HitNormal, HitTime);
// Location on bounds with rotation.
OutIntersectionPoint = InBoxTransform.TransformVector(HitLocation - CenterPoint) + CenterPoint;
if (IsValid(InDebugWorld)) {
// Hit location aligned with the FBox used for bounds.
DrawDebugSphere(InDebugWorld, HitLocation, 30, 16, FColor::Orange, bPersistDebug, DebugLifetime, 0, 15.f);
// Hit location projected on rotated box.
DrawDebugSphere(InDebugWorld, OutIntersectionPoint, 30.f, 16, FColor::Red, bPersistDebug, DebugLifetime, 0, 15.f);
}
}
if (IsValid(InDebugWorld)) {
// FBox with no rotation.
DrawDebugBox(InDebugWorld, CenterPoint, InBoxExtent3D, FColor::White, bPersistDebug, DebugLifetime, 0, 4.f);
// Box with rotation
DrawDebugBox(InDebugWorld, CenterPoint, InBoxExtent3D, InBoxTransform.GetRotation(), FColor::Yellow, bPersistDebug, DebugLifetime, 0, 4.f);
// CenterPoint
DrawDebugSphere(InDebugWorld, CenterPoint, 30.f, 16, FColor::White, bPersistDebug, DebugLifetime, 0, 30.f);
if (bInBox) {
// TransformedPointToTest
DrawDebugSphere(InDebugWorld, TransformedPointToTest, 30.f, 16, FColor::Cyan, bPersistDebug, DebugLifetime, 0, 30.f);
}
}
return bInBox == false;
}
bool UIntersectionUtils::GetIntersectionFrom2DPlaneCenterToEdge(const FVector2D& InPointToTest, const FVector2D& InPlaneLocation, const FVector2D& InPlaneExtent, float InPlaneYaw, FVector2D& OutIntersectionPoint, const UObject* const InDebugWorldContext) {
const bool bPersistDebug = false;
const float DebugLifetime = 3.f;
const float DCosYaw = UKismetMathLibrary::DegCos(InPlaneYaw);
const float DSinYaw = UKismetMathLibrary::DegSin(InPlaneYaw);
// Add the location offset of the plane.
const FVector2D MovedPointToTest = InPointToTest - InPlaneLocation;
// Rotate to the planes rotation.
const FVector2D TransformedPointToTest = FVector2D(
(DCosYaw * MovedPointToTest.X) + (DSinYaw * MovedPointToTest.Y)
, (DCosYaw * MovedPointToTest.Y) - (DSinYaw * MovedPointToTest.X)
);
// If within bounds, we dont get an intersection. Then we should return.
if (FMath::Abs(TransformedPointToTest.X) <= InPlaneExtent.X
&& FMath::Abs(TransformedPointToTest.Y) <= InPlaneExtent.Y
) {
OutIntersectionPoint = FVector2D::ZeroVector;
return false;
}
const FVector2D TransformedPointWithinBounds = TransformedPointToTest / (
// Check if inside bounds.
FMath::Abs((TransformedPointToTest / (FMath::Abs(TransformedPointToTest.X) / InPlaneExtent.X)).Y) <= InPlaneExtent.Y
? FMath::Abs(TransformedPointToTest.X) / InPlaneExtent.X
: FMath::Abs(TransformedPointToTest.Y) / InPlaneExtent.Y
);
OutIntersectionPoint = FVector2D(
(UKismetMathLibrary::DegCos(InPlaneYaw * -1) * TransformedPointWithinBounds.X) + (UKismetMathLibrary::DegSin(InPlaneYaw * -1) * TransformedPointWithinBounds.Y)
, (UKismetMathLibrary::DegCos(InPlaneYaw * -1) * TransformedPointWithinBounds.Y) - (UKismetMathLibrary::DegSin(InPlaneYaw * -1) * TransformedPointWithinBounds.X)
) + InPlaneLocation;
const UWorld* InDebugWorld = IsValid(InDebugWorldContext) ? InDebugWorldContext->GetWorld() : nullptr;
if (IsValid(InDebugWorld)) {
// Line from plane center to point to test (original)
DrawDebugLine(InDebugWorld, FVector(InPlaneLocation, 0.f), FVector(InPointToTest, 0.f), FColor::White, bPersistDebug, DebugLifetime, 0, 10.f);
// Box with rotation
DrawDebugBox(InDebugWorld, FVector(InPlaneLocation, 0.f), FVector(InPlaneExtent, 1.f), FRotator(0.f, InPlaneYaw, 0.f).Quaternion(), FColor::Yellow, bPersistDebug, DebugLifetime, 0, 15.f);
// Transformed intersect point
DrawDebugSphere(InDebugWorld, FVector(OutIntersectionPoint, 0.f), 50.f, 16, FColor::Red, bPersistDebug, DebugLifetime, 0, 20.f);
}
return true;
}
*Edit Fixed bug where UKismetMathLibrary::IsPointInBox did not use the transformed InPointToTest, basically causing the check to be done on the unrotated box.