Reverse Module3D

Module3D is really useful for Object Previews, but right now i want to make the camera itself adjusted its position from the model so the whole model is visible.
Or in other words, The Model doesn’t Move or Anything, the Camera is the one is Moving.
Any suggestions or maths? It would be really useful if the code was associated with Module3D too, because it would be easier to understand for me.

3 Likes

If you want the camera to focus on one part, you can set its CameraSubject to that part. If you can select a part that is close to the center of the model all the time, that’s great! Your work stops here.

If you do not have a part close to the center of the model, you will need a more complicated solution, like an AABB algorithm (Axis-Aligned Bounding Box) and position the camera around its center (possibly also using CameraSubject)

Both @Maximum_ADHD and @zeuxcg have made some in RbxLua.

Clonetrooper’s: https://pastebin.com/nxrpewtz
Zeuxcg’s: https://gist.github.com/zeux/1a67e8930df782d5474276e218831e22

If you want the camera to zoom out to so the player can see the entire model at once, one way is to take the size of the AABB given above and move it AABB.Size.magnitude/2/math.tan(math.rad(Camera.FieldOfView)) away from the center, which you can do with CFraming, if you are using Scriptable camera, or use the Player’s CameraMinZoomDistance if you are not.

6 Likes

alright, how do i use the Region3 that the function returns? Never used Region3 anyway

You have to take the average of the min value and max value to find the center, and take the difference to find the size. You do not have to use a Region3 if you do not want to, you can remove that part and have it return 2 Vector3 values.

This is sort of what I do.

This code isn’t great, but it’s calculating how far out the camera has to zoom to capture an item with the given size, passed in as a Vector3. This is an approximation to the real solution, which should project points to the screen and then zoom out accordingly.

A benefit to this code is that it maintains zoom while ignoring orientation which is important. However, the calculation for size does mean that sometimes items end up off-screen.

This code is provided as a sample.

--- Calculates how far back the camera needs to zoom out to fit an item of size on the screen
-- @tparam Vector3 size
-- @tparam Vector2 percentOfScreen
function SelectorCameraCalculator:GetZoomOffset(cameraState, size, percentOfScreen)
	local distance = 25

	local calculateForSize = math.max(size.x, size.y, size.z)
	local viewportSize = Workspace.CurrentCamera.viewportSize
	local aspectRatio = viewportSize.x / viewportSize.y
	local heightFactor = math.tan(math.rad(cameraState.FieldOfView)/2)
	local widthFactor = aspectRatio*heightFactor

	local depth = 0.5*calculateForSize/(percentOfScreen.x*widthFactor)
	local depthTwo = 0.5*calculateForSize/(percentOfScreen.y*heightFactor)

	distance = math.max(distance, depth, depthTwo)
	return distance
end
3 Likes

I’ll point out that zeuxcg’s implementation is way faster than mine.

Not relevant to OP, but relevant to anyone who’s looking for code that finds the AABB of a model (which was how I found the thread): Here’s a hacky piece of code that finds the AABB of a model faster for large part counts:

local function ModelAABB2(model)
	local originalPrimaryPart = model.PrimaryPart
	local fakeCenter = Instance.new("Part")
	fakeCenter.Size = Vector3.new(0, 0, 0)
	local center, extents
	if originalPrimaryPart then
		fakeCenter.CFrame = CFrame.new(originalPrimaryPart.Position)
		fakeCenter.Parent = model
		model.PrimaryPart = fakeCenter
		center = model:GetModelCFrame().p
		extents = model:GetExtentsSize()
		model.PrimaryPart = originalPrimaryPart
	else
		local calcCenter = model:GetModelCFrame()
		fakeCenter.CFrame = CFrame.new(calcCenter.p)
		fakeCenter.Parent = model
		model.PrimaryPart = fakeCenter
		center = model:GetModelCFrame().p
		extents = model:GetExtentsSize()
		model.PrimaryPart = nil
	end
	
	fakeCenter:Destroy()
	local min, max = center-extents/2, center+extents/2
	
	return min, max
end

I use a small, un-rotated part and set that as the model’s PrimaryPart temporarily. This causes :GetModelCFrame() to return the center of the model’s AABB and :GetExtentsSize() to return the size of the AABB (instead of the size of rotated bounding box). Doing this should be fine as long as temporarily setting the primary part of the model doesn’t cause any side effects.

2 Likes