Issue with viewport frame width scale

I’m currently writing a piece of code that would return the minimum distance you need to fully encapsulate a model at any camera orientation.

At first this seemed simple enough: Find the minimum radius that would fully encapsulate the subject model and then use some basic trig to calculate the distance from the camera.

2021-07-11_12-17-37

sin(fov2) = radius / distance
distance = radius / sin(fov2)

This works great when the height of the viewport frame is larger or equal to its width, but when it’s not the sides of the object will get cut off.

RobloxStudioBeta_2021-07-11_12-24-08 2021-07-11_12-24-34

width == height vs. width < height

As such we need to calculate a corrected distance

2021-07-11_12-20-13

a = PI / 2 - FOV / 2
h2 = sin(a) * radius
d = cos(a) * radius

hw = viewportFrameSize.Y / viewportFrameSize.X

correctedDistance =  (h2 * hw) / tan(fov2) + d

2021-07-11_12-25-58 2021-07-11_12-26-14

width == height vs. width < height

This is the full code:

function module.GetModelFitDistance(model, vpf, camera)
	local modelCFrame, modelSize = model:GetBoundingBox()
	
	local vpfSize = vpf.AbsoluteSize
	local hw = math.max(1, vpfSize.Y / vpfSize.X)
	
	local fov2 = math.rad(camera.FieldOfView / 2)
	local alpha = math.pi/2 - fov2

	-- just assume displacement == 0 for now
	local displacement = (camera.Focus.Position - modelCFrame.Position).Magnitude
	local radius = ((modelSize.Magnitude / 2) + displacement)
	local h2 = math.sin(alpha) * radius
	local d = math.cos(alpha) * radius
		
	return (h2 * hw) / math.tan(fov2) + d
end

Right, so now with all that preamble out of the way onto my actual question!

I’m running into a weird issue where certain camera orientations leave ample room from the edges when the frame is square. However, these same orientations get cut off when width < height.

To me it seems to make no sense, we calculated a static distance that is guarnteed to fit the worst case scenario (and it seems to successfully do that), yet it fails when it should be nowhere near the maximum constraint?

2021-07-11_12-31-05 2021-07-11_12-31-34

Notice the left most side is starting to get cut off

So that makes me think this is caused by 1 of 2 things:

  1. I’ve done my math wrong (which I find hard to believe b/c as per earlier examples it fits perfectly)
  2. There’s some weird scaling factor going on w/ viewport frames that I’m not accounting for.

Any help would be greatly appreciated. I’ve attached the place file I used for the images here:

Fit viewport frame 2.rbxl (26.8 KB)

For refrence I was toggling the width of the viewport frame between 200 and 100 and the orientation around the y-axis between 90 and 45 degrees.

7 Likes

I am gonna go with this theory since Roblox’s viewport system kinda sucks. I have experienced the same problem a few months ago and I couldn’t really make it out, even searching for fixes.

1 Like

Here are my findings so far.

I get the same results with all 3 FOV modes, but it’s definitely an FOV problem. First image is 70 FOV, second image is 1 FOV. (circle shows the bounding box radius)


Furthermore, this happens without a ViewportFrame.

1 Like

Hmm…

The fact that the issue gets magnified at higher fovs and minified at lower fovs makes me def think this is an issue w/ my math. I guess my initial assumptions are incorrect somehow. I’ll have to go back to square one…

2 Likes

This might be of use to you. Roblox: Fit model perfectly into ViewportFrame - YouTube

Unfortunately the video sets the viewport size to fit the model where as I’m trying to force the camera distance to fit a fixed size viewport.

Thanks though!

2 Likes

Alright, I believe I successfully figured it out.

So the trick is to calculate the horizontal fov and then use that to calculate the distance.

function module.GetModelFitDistance(model, vpf, camera)
	local modelCFrame, modelSize = model:GetBoundingBox()
	
	local vpfSize = vpf.AbsoluteSize
	local wh = math.min(1, vpfSize.X / vpfSize.Y)

	local yfov2 = math.rad(camera.FieldOfView / 2)
	local xfov2 = math.atan(math.tan(yfov2) * wh)

	local displacement = (camera.Focus.Position - modelCFrame.Position).Magnitude
	local radius = ((modelSize.Magnitude / 2) + displacement)

	return radius / math.sin(xfov2)
end

RobloxStudioBeta_2021-07-11_14-57-24 2021-07-11_14-57-31

23 Likes