Zoom in perspective exactly to objects size

I have used this dirty hack (viewport.zoom .75) in code below to approximately get the viewport of the perspective to fill the viewport as best it can and also reset the VP the way I like.

	viewport.ResetAllViews() --resets to Max Standard
	actionMan.executeAction 0 "311" --frames all viewports
	viewport.zoom .75 --fix to make perspective view appear closer
	viewport.setGridVisibility #all false --turns off all grids
	actionMan.executeAction 0 "40829" --show statistics
	actionMan.executeAction 0 "63547" -- Views: Viewport Materials Display as Realistic with Maps

This works fine but I was searching for a more accurate solution and I got a fn from Dennis T on another forum to get the bounding size of objects in a scene. I've searched hi and low for the answer on how the viewport.ZoomToBounds System Global Variable actually works in relation to this and also how to get the zoom to work. Searching now 3 hours and no luck.


global ZoomToObjBB
fn ZoomToObjBB = (
	viewport.ResetAllViews() --resets to Max Standard
	actionMan.executeAction 0 "311" --frames all viewports
	--viewport.zoom .75 --fix to make perspective view appear closer
	viewport.setGridVisibility #all false --turns off all grids
	actionMan.executeAction 0 "40829" --show statistics
	actionMan.executeAction 0 "63547" -- Views: Viewport Materials Display as Realistic with Maps	
	nodes = for node in objects where iskindof node GeometryClass collect node
	if nodes.count > 0 do
		view = viewport.activeViewport
		local bmin = [1e9,1e9,0], bmax = [-1e9,-1e9,0]
		gw.setTransform (matrix3 1)
		transPoint = gw.hTransPoint
		for node in nodes do
			mesh = snapshotasmesh node
			for v=1 to mesh.numverts do
				vp = transPoint (GetVert mesh v)
				if vp.x < bmin.x do bmin.x = vp.x
				if vp.x > bmax.x do bmax.x = vp.x
				if vp.y < bmin.y do bmin.y = vp.y
				if vp.y > bmax.y do bmax.y = vp.y
			free mesh
		w = (bmax.x - bmin.x) as integer
		h = (bmax.y - bmin.y) as integer
		print bmin.x
		print bmin.y
		print bmax.x
		print bmax.y
		print w
		print h
		viewport.ZoomToBounds false (point3 w h 0) (point3 w h 0)

Any help would be greatly appreciated from you experts out there :)


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Maarten's picture

At long last, it is done

Hello, been working on this for a while now. Finally got it working (tried quite a few methods). The script goes through all the points in a given mesh and saves the positions of each of these points. The script then checks the translation of these points relative to the camera field of view lines (camera FOV Cone lines). The points which are furthest away from these lines are the points which the camera field of view lines should intersect. I then calculate the intersection points between the two FOV lines (one time for xy plane and another time for yz plane). Then I create a camera and place it on the correct coordinates depending on the calculation results (for line intersection points).

You can change the *teapot* wildcard in line to change what object is selected (change the wildcard so it selects the obj(s) of your choice.

The script corrects for change of render resolution automatically. For 'padding' you can add a percentage increase to the camera's distance from the object so you'll have a bit of space around the object (atm the viewport edges are perfectly aligned with the selected object verts so no space around obj). If you want some more detailed info on how it works I'll be happy to explain.

You could probably modify the code to link it to a shortcut key (activate on keypress) and make it zoom the viewport rather then place a correctly zoomed camera if this is needed for your application.

Hope this helps.

--Set initial variables
zoom_points = #(1,1,1,1,1,1,1,1,1,1,1,1)
cam_FOV = (45 as float) ; cam_FOV_Z = (30 as float) --these 2 values will get replaced with the actual camera's FOV angles in line 10-17 (see below)
--Create/Rotate Freecam (rotate so that the freecam faces in the 'front' direction and is positioned on the y-axis
freecam = freecamera()
rotate freecam (eulerangles  90 0 0)
--Measure/Update Cam FOV Angles (Cam Vertical FOV + Horizontal FOV)
fn GetCamVFOV theCamera =
	local r_aspect=(renderWidth as float)/renderHeight
	cam_FOV_Z = 2.0*atan(tan(theCamera.fov/2.0)/r_aspect)
	cam_FOV = theCamera.curfov
GetCamVFOV freecam --run function for getting Cam FOVs
fn persp_zoom align_side =(--start fn persp_zoom
	--initial variable declaration
 delta_y ; delta_y_old=999999
for obj in geometry do (
	converttomesh obj
	if matchpattern obj.name pattern:"*teapot*" do(
		for v=1 to getnumverts obj do (
			--print (getvert obj v)
			vert_x = (getvert obj v)[1] --save vertex x coordinate
			vert_y = (getvert obj v)[2] --save vertex y coordinate
			vert_z = (getvert obj v)[3] --save vertex z coordinate
			--I am using  formula which represents each of the camera's field of view lines (no actual line is drawn, just using a formula which represents this line to do calculations).
			--With these formulas I calculate the distance in y direction (also known as translation of a line) of each point in the mesh relative to each field of view line.
			--line starts in 0,0,0
			--line angle = (cam_FOV) / 2
			--1. CALC. Delta_Y
			if (align_side == 1) then (delta_y = -tan(90-(cam_FOV/2))* vert_x + vert_y )	--working
			if (align_side == 2) then (delta_y = vert_y - tan(90+(cam_FOV/2))*vert_x ) -- Working
			if (align_side == 3) then (delta_y = -tan(90-(cam_FOV_Z/2))* vert_z  + vert_y )--working	
			if (align_side == 4) then (delta_y = vert_y - tan(90+(cam_FOV_Z/2))*vert_z )--working
			--Collect lowest/highest Delta_y value
			--The points which have the lowest translation value in y direction are the points of the mesh on which I need to zoom the camera (translation of line upward = positive /higher translation value )
			--For each point in the mesh I calculate its translation relative to the first Field of view line (right), I also do this for each point in the mesh relative to the second FOV line (left) and so on (for all 4 FOV lines)
			--The angle of these FOV lines is read from the camera's FOV angles (see above)
			--Zoom_points is an array which contains important information about the points on which the camera needs to zoom (the points which the cam FOV lines should go through).
			--The Zoom_points array is formated like this:
				-- # ( delta_y , vertex number, vertex position , delta_y , vertex number, vertex position, delta_y , vertex number, vertex position, delta_y , vertex number, vertex position )
				--	    l......................Right side zoom point.....................l	 l......................Left side zoom point...................l  l......................Top side zoom point.....................l    l......................Bottom side zoom point.....................l	
				--In the Codeblock below (line 59-72) the zoom_points array is filled with information regarding the zoom points according to the array format explained in the lines above 
				if delta_y < delta_y_old do (
					if	(align_side == 1) then (zoom_points[1] = delta_y ; zoom_points[2] = v ; zoom_points[3] = (getvert obj v) )	
					if	(align_side ==2) then (zoom_points[4] = delta_y ; zoom_points[5] = v ; zoom_points[6] = (getvert obj v) )
					if (align_side == 3) then (zoom_points[7] =( vert_z - (tan(cam_FOV_Z/2)*vert_y) ) ; zoom_points[8] = v ; zoom_points[9] = (getvert obj v) ) --Recalculating delta_y value in order to get correct output
					if	(align_side == 4) then ( zoom_points[10] =(-(tan(-cam_FOV_Z/2)*vert_y) + vert_z ) ; zoom_points[11] = v ;zoom_points[12] = (getvert obj v) ) --Recalculating delta_y value in order to get correct output
					delta_y_old = delta_y --set last value for delta_y equal to delta_y_old so the next value can be compared relative to this value of delta_y
					)--end if delta_y < delta_y_old do
		)--end for v=1 to getnumverts obj do
	)--end if matchpattern obj.name pattern:"*" do
)--end for obj in geometry do
format "zoom_points array = % \n" zoom_points --format zoom points array (for testing)
)--end fn persp_zoom
persp_zoom 1 ; persp_zoom 2 ; persp_zoom 3 ; persp_zoom 4 --Call persp_zoom function for each side of the object (1 = right, 2 = left, 3 = top, 4 = bottom) --Each time the function is called the Zoom_points array is updated (point info is stored)
	--Calculate line intersection points
	--Calc. Intersection coordinates in XY plane (x,y)
	x_intersect = ( zoom_points[4] -  zoom_points[1] ) / ( tan(90-(cam_FOV/2) ) - tan(90+(cam_FOV/2) ) ) --Calculate X coordinate of intersection
		format "x_intersect = % \n" x_intersect
	y_intersect = tan(90-cam_FOV/2)*x_intersect + zoom_points[1] --Calculate Y coordinate of intersection
		format "y_intersect = % \n" y_intersect
	--Calc. Intersection coordinates in ZY plane (z,y)
	zy_intersect = ( zoom_points[10] - zoom_points[7] ) / ( tan(cam_FOV_Z/2) - tan(-cam_FOV_Z/2) ) --Calculate Z coordinate of intersection
		format "z_intersect = % \n" zy_intersect
	z_intersect = tan(cam_FOV_Z/2)*z_intersect + zoom_points[7] --Calculate ZY coordinate of intersection
		format "zy_intersect = % \n" z_intersect
point pos: [x_intersect, y_intersect, 0] --create points at intersection points (for visual referance/testing)
point pos: [0, z_intersect, zy_intersect]
if (abs(y_intersect) > abs(zy_intersect)) then (--start if (abs(y_intersect) > abs(zy_intersect)) then 
	freecam.pos = [x_intersect, y_intersect, z_intersect] --change freecam position so that zoom level/alignment is correct (scenario 1)
	freecam.pos = [x_intersect, zy_intersect, z_intersect]  --change freecam position so that zoom level/alignment is correct (scenario 2)
	)--end if (abs(y_intersect) > abs(zy_intersect)) then 
perspective_zoom_test_scene_3.max 276 KB
line_obj_measurer_050_working_zoom__creates_freecam_.ms 3.12 KB
3dwannab's picture

Thanks. It's a step forward

Thanks. It's a step forward but in a destructive way unfortunately.

Thanks for trying to get it to work but this wouldn't work for my use. I clean up folders using a batch script which runs selected scripts on max file and this would be too destructive and slow on objects such as trees etc.

Maarten's picture

some study material

And here we have some study material regarding perspective projection


Maarten's picture

Use Hold/Fetch

Perhaps you can use hold/fetch functions in 3ds max to make it less destructive, hold before zoom operation and fetch after to get the original model state back. If you remove the print/format lines from the script it will be faster. probably won't take more than a few seconds per model (depending on mesh complexity). You can quite easily remove the lines where a new freecamera is created and aligned. You could then replace these lines with a few simple lines to take a camera in your scene (or all cameras) and align them to the correct zoom positions.

The camera in this zoom script is also aligned with one of the axis (it has a standard location) which I forgot about (was writing this as a part of a personal project). But I should be able to modify it so that it works with cameras in all angles. It is a matter of changing a few formulas and variables. Will take some time but is definitely possible.

Could you explain what about my method is 'destructive'?
You could perhaps modify the function so that it only goes through a specified part of the verts. Could you explain in more detail what exactly you want to do with the script, Are you trying to zoom in the camera on the object in your scenes? perhaps I can help.

I am not sure what other method could be used to zoom 100% accurately on the object. If you could somehow only look at the points which are most likely going to be close to the edges of the screen (close to the camera's field of view lines) this would be nice but this is hard to define.
If you could determine which points in the mesh the viewport should be zoomed on you could make it faster probably. Not sure how one would do this though. We can try though :) So the plan would be to find a way to determine which points lie most to the left, right, top and bottom of the current(/specified) viewport. Then the camera can be zoomed in on these points (This last zooming in on the points part would be the same as in the script I uploaded a few days ago). We cannot rely on the screen coordinate system to find the correct points bacause this does not take perspective into account...

If you can think of another way to define these 'zoom points' I'd be happy to hear it from you :) I've added an image to show what I mean by zoom points. (see red points on model in image) Note that these points are not always the object's max and min x,y,z values (try setting up a scene and rotating the object if you want to see for yourself). They are in a way the object's maximum and minimum values but relative to the camera's FOV (Field of view) Lines. I tried getting the object's max and min values in this coordinatesystem but could not get it to work, you can try also, perhaps you'll get better results. By the way, I think 3ds max's obj.max.x (and similar) functions use a similar method as I am using with the 'perspective zoom' script , as in the function looks at all the points in the object (x value in the case of obj.max.x) and returns (the function returns) the maximum value of x in this array. I could be wrong on this though :P

The zoom should not be 100% accurate per se because you always want some room around your model (aka padding). You could perhaps go through the models in steps of 10 vertices, which would be less accurate but would yield relatively good results if the vertex density of the models is somewhat uniform.
Perhaps you could also make the amount of vertecis the script goes over(/skips) each step depend on the distance of the last vertex it calculated (the distance relative to one of the field of view lines) so that the closer it gets to the FOV line the smaller the steps (and it will be more accurate closer to the FOV lines). But for that I'd need a better understanding of vertex order in 3d models.

Anyways, good luck and please let me know if you know/find something :)


zoom_points.png 27.42 KB
3dwannab's picture

Wow!! Nice write up there

Wow!! Nice write up there Maarten. I'm busy with a script at the moment but hopefully I have enough spare time to procrastinate on this again :P

I will definitely come back to this at some point but for now it's not no.1 on the list of things to do.

Good work Maarten.

Maarten's picture

Had any luck with this?

I am working on a very similar problem in maxscript (zoom exactly on obj.size in perspective). I think I found a good method but will have to test it and see if I can optimize it, will share results when some progress has been made.

3dwannab's picture

I gave up after a while. I

I gave up after a while. I was hoping someone more cleverer than myself would jump on board! :P

I look forward to seeing your findings.

3dwannab's picture


I've still had no luck with this.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.