Custom Mesh FOV

In UDK, the UDKSkeletalMesh had an FOV option whereby we could set a custom FOV that the mesh would be rendered at. Is there such an option in UE4? I don’t see it in the properties of my skeletalmeshcomponents for my blueprint. Does it have to be done by code? Does it exist at all?

Sooo… I guess this isn’t possible yet? I can’t find info on it anywhere. :frowning:

I could not find anything related to FOV and SkeletalMesh in API refrence so i guess not.

I know that all classes that starts with UDK and are part of UDKBase are not really part of UE3 framework those some special things made for UDK, so most likely UE4 didn’t inherited it. It’s most likely implementable but extra requires C++ coding and override of USkeletalMeshComponent

Alright, thanks for the reply. I don’t think I’m skilled enough to implement it myself unfortunately, so I guess I’ll have to do without :frowning:

I would need it too. Pretty handy when you have a weapon/Cockpit to display in the foreground…

Yes, Shadowriver was right. Indeed override GetRenderMatrix of USkeletalMeshComponent should work. The following code is the code which I was developed for our game. Please notice I used magic number 1920/1080 which is our game resolution. Feel free change it to whatever fit to your game.

    APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	if (playerController)
	{
		ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(playerController->Player);

		if (LocalPlayer != NULL && LocalPlayer->ViewportClient != NULL && LocalPlayer->ViewportClient->Viewport != NULL)
		{
			FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
				LocalPlayer->ViewportClient->Viewport,
				GetWorld()->Scene,
				LocalPlayer->ViewportClient->EngineShowFlags)
				.SetRealtimeUpdate(true));

			FVector ViewLocation;
			FRotator ViewRotation;
			FSceneView* SceneView = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, LocalPlayer->ViewportClient->Viewport);

			float MinZ = SceneView->NearClippingDistance;
			float MaxZ = MinZ;

			// Avoid zero ViewFOV's which cause divide by zero's in projection matrix
			float MatrixFOV = FMath::Max(0.001f, WeaponFOV) * (float)PI / 360.0f;

			FMatrix projMatrix = FReversedZPerspectiveMatrix(
					MatrixFOV,
					MatrixFOV,
					1.0f,
					1920.0f / 1080.0f,
					MinZ,
					MaxZ
					);

			FScaleMatrix ClipSpaceFixScale(FVector(1.0f, 1.0f, 1.0f - 0.0f));
			FTranslationMatrix ClipSpaceFixTranslate(FVector(0.0f, 0.0f, 0.0f));
			projMatrix =  projMatrix * ClipSpaceFixScale * ClipSpaceFixTranslate;

			FMatrix adjustedViewProjectMatrix = SceneView->ViewMatrices.ViewMatrix * projMatrix;
			FMatrix inverseOldViewProjectMatrix = SceneView->InvViewProjectionMatrix;
			FMatrix adjTransform = ComponentToWorld.ToMatrixWithScale() * adjustedViewProjectMatrix * inverseOldViewProjectMatrix;

			return adjTransform;
		}
		else
		{
			return Super::GetRenderMatrix();
		}
	}
	else
	{
		return Super::GetRenderMatrix();
	}

Awesome stuff!

Just had to do a small check to ensure that “SceneView” is not null - seems that for a few frames at startup, it will be null (depending on when you spawn your meshes). Otherwise, works perfectly (even with attachments/sockets).

Thanks very much for sharing.

Thanks a lot for pointing out a potential risk.

Hi frankmli,

It seems that in 4.9 this is unfortunately causing a crash.

It happens around line 16 (where the SceneView is created). It first failed on an assertion which checked that the Times were set (I got around that by retrieving the World Time, Delta Time and Game Time from GetWorld() and setting these on the construction params. However it crashes at a later stage as well and I’m struggling to fix it.

So I suppose my question is whether you’ve managed to get this working in 4.9 yet?

In any case, I’ll keep bashing my head against it and hope I figure it out eventually.

Also thanks very much again for sharing this!

Cheers.

Hi, Staticvoidlol

We don’t have such issue at all here. Here are the screenshot from our game which running at version UE4.9. As what you described in your message, it seems line 8-12 might be issue which constructing messed viewFamily.

Good luck.

Cheers,
Frank

Hi Frank,

Thanks very much for getting back to me.

Interesting that it works perfectly for you.

It seems mine is only crashing when running the game standalone (not PIE), and it crashes on GetShadowIndex() in the class TConsoleVariableData (as shown in the screen shot).

I honestly have no idea what this function is supposed to be doing other than checking that it’s happening on the main game thread

Have you checked that yours works fine in non-editor runtime?

Cheers!

I got this here too in debug build. Hope I can find what wrong it is.

Frank

basically what it means is in GetRenderMatrix which is called in render thread invokes function LocalPlayer->CalcSceneView which should be only called in game thread. It is the issue. I need a way to get same information through render thread friendly function.

Ah ok, I suppose that makes sense. Thanks for checking it out.

Thanks staticvoidlol for point out the issue. The last code I wrote which invoke a game thread function in render thread. The following code is my latest fix. Although it looks clumsy about calculating project/view matrix for viewport, it works perfectly here.

FMatrix CustomFOVMeshComponent::GetRenderMatrix() const
{
	//Get camera perspectiveMatrix
	APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	if (playerController)
	{
		ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(playerController->Player);

		if (LocalPlayer != NULL && LocalPlayer->ViewportClient != NULL && LocalPlayer->ViewportClient->Viewport != NULL)
		{
			FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
				LocalPlayer->ViewportClient->Viewport,
				GetWorld()->Scene,
				LocalPlayer->ViewportClient->EngineShowFlags)
				.SetWorldTimes(0.0f, 0.0f, 0.0f)
				.SetRealtimeUpdate(false));

			FVector ViewLocation;
			FRotator ViewRotation;

			float MinZ = 3.0f;
			float MaxZ = MinZ;

			// Avoid zero ViewFOV's which cause divide by zero's in projection matrix
			float MatrixFOV = FMath::Max(0.001f, WeaponFOV) * (float)PI / 360.0f;

			FMatrix projMatrix = FReversedZPerspectiveMatrix(
					MatrixFOV,
					MatrixFOV,
					1.0f,
					LocalPlayer->Size.X * LocalPlayer->ViewportClient->Viewport->GetSizeXY().X / (LocalPlayer->Size.Y * LocalPlayer->ViewportClient->Viewport->GetSizeXY().Y),
					MinZ,
					MaxZ
					);

			FScaleMatrix ClipSpaceFixScale(FVector(1.0f, 1.0f, 1.0f - 0.0f));
			FTranslationMatrix ClipSpaceFixTranslate(FVector(0.0f, 0.0f, 0.0f));
			projMatrix =  projMatrix * ClipSpaceFixScale * ClipSpaceFixTranslate;

			FMatrix viewMatrix;
			FMatrix invViewProjectionMatrix;

			GetViewMatrices(viewMatrix, invViewProjectionMatrix);

			FMatrix adjustedViewProjectMatrix = viewMatrix * projMatrix;
			FMatrix inverseOldViewProjectMatrix = invViewProjectionMatrix;
			FMatrix adjTransform = ComponentToWorld.ToMatrixWithScale() * adjustedViewProjectMatrix * inverseOldViewProjectMatrix;

			return adjTransform;
		}
		else
		{
			return Super::GetRenderMatrix();
		}
	}
	else
	{
		return Super::GetRenderMatrix();
	}
}

void CustomFOVMeshComponent::GetViewMatrices(FMatrix& viewMatrix, FMatrix& invViewProjectionMatrix) const
{
	APlayerController* playerController = GetWorld()->GetFirstPlayerController();
	ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(playerController->Player);

	//Get View Origin
	FVector ViewOrigin;
	FRotator ViewRotation;
	playerController->GetPlayerViewPoint(/*out*/ ViewOrigin, /*out*/ ViewRotation);

	FMatrix ViewRotationMatrix = FInverseRotationMatrix(ViewRotation) * FMatrix(
		FPlane(0, 0, 1, 0),
		FPlane(1, 0, 0, 0),
		FPlane(0, 1, 0, 0),
		FPlane(0, 0, 0, 1));

	if (!ViewRotationMatrix.GetOrigin().IsNearlyZero(0.0f))
	{
		ViewOrigin += ViewRotationMatrix.InverseTransformPosition(FVector::ZeroVector);
		ViewRotationMatrix = ViewRotationMatrix.RemoveTranslation();
	}

	// Calculate view matrix
	viewMatrix = FTranslationMatrix(-ViewOrigin) * ViewRotationMatrix;

	// Calculate project matrix
	int32 X = FMath::TruncToInt(LocalPlayer->Origin.X * LocalPlayer->ViewportClient->Viewport->GetSizeXY().X);
	int32 Y = FMath::TruncToInt(LocalPlayer->Origin.Y * LocalPlayer->ViewportClient->Viewport->GetSizeXY().Y);
	uint32 SizeX = FMath::TruncToInt(LocalPlayer->Size.X * LocalPlayer->ViewportClient->Viewport->GetSizeXY().X);
	uint32 SizeY = FMath::TruncToInt(LocalPlayer->Size.Y * LocalPlayer->ViewportClient->Viewport->GetSizeXY().Y);

	FIntRect UnconstrainedRectangle = FIntRect(X, Y, X + SizeX, Y + SizeY);

	FSceneViewProjectionData ProjectionData;
	ProjectionData.SetViewRectangle(UnconstrainedRectangle);

	FMinimalViewInfo OutViewInfo;

	if (playerController->PlayerCameraManager != NULL)
	{
		OutViewInfo = playerController->PlayerCameraManager->CameraCache.POV;
		OutViewInfo.FOV = playerController->PlayerCameraManager->GetFOVAngle();
		playerController->GetPlayerViewPoint(/*out*/ OutViewInfo.Location, /*out*/ OutViewInfo.Rotation);
	}
	else
	{
		playerController->GetPlayerViewPoint(/*out*/ OutViewInfo.Location, /*out*/ OutViewInfo.Rotation);
	}

	FMinimalViewInfo::CalculateProjectionMatrixGivenView(OutViewInfo, LocalPlayer->AspectRatioAxisConstraint, LocalPlayer->ViewportClient->Viewport, /*inout*/ ProjectionData);

	FMatrix ProjMatrix = ProjectionData.ProjectionMatrix;

	FViewMatrices ViewMatrices;
	ViewMatrices.ViewMatrix = viewMatrix;
	ViewMatrices.ProjMatrix = ProjMatrix;
	invViewProjectionMatrix = ViewMatrices.GetInvViewProjMatrix();
}

The fixed version is in another answer!!

Hi Frank,

Again, I must say that I appreciate your effort and kindness in sharing this immensely! It is much appreciated - if there is anywhere I can donate to your project (or if you’d like to put this on the marketplace I’d buy it in an instant) please let me know.

However, I am having a bit of trouble with this new version - you can see the problem in this video.

It seems that my mesh now doesn’t follow my character properly - it seems to be always positioned between my character and the world origin. I would suspect that there are some values getting pulled through as zero where it shouldn’t be. This is purely a hunch as it seems to point to a Vector(0,0,0) (which is the world origin) affecting the transforms.

Do you have any idea what might be causing this behaviour? I’ve looked through each line of the code best I can, but I’m a bit lost here as I can’t find anything that is zero where it obviously shouldn’t be.

If you have some time to look at this I would be very grateful.

Thanks very much!

Hi,

I don’t have such issue here. If the mesh is always in the world origin, set a break point at line 47, check ComponentToWorld.ToMatrixWithScale() if the world matrix still keep the translation.

Hi Frank,

Thanks very much for having a look.

It seems that ComponentToWorld does keep the translation correctly (it’s equal to the location of the mesh).

I’m getting the

at the end of the “GetViewMatrices()” function:

Does anything look off to you?

Hi,

This might be the problem

It seems you simply get the view point location and rotation from current actor location and rotation. Basically, you need to fill out the information with the camera which viewport is rendered from. Hope it is helpful