Change camera Field of View based on objects size?


#1

Hi, I want to bulk render all of the selected objects in the scene (Think of in-game items) I would like them to stay at the same angle.

I have a system figured out for rendering them, but I want the camera to automatically center onto the next object in the list, but I am stuck on trying to keep the size consistent because It would look odd if some of the items were smaller than the other by changing the field of view for the camera.

Example

I believe someone posted an xpresso script of what I want but I do not have access to the file

Can I do this via python?


#2

I’ve recreated the scene from the Cafe screenshot, but I don’t think it’s what you want.

cam-plane.c4d (198.2 KB)


#3

Hi,

I also came to the conclusion, that the referenced thread probably does not contain a solution for your problem. It’s more the opposite solution. It constructs a plane, which exactly matches the field of view in a certain distance.

But if I understood you correctly, you have a bunch of arbitrary objects and you want to change the camera in a way, so each object is roughly positioned in the same way in front of the camera. Right?

I quickly hacked something together, which is probably utter bs, if one looks into the details. Currently I have no time to polish it and to look into the corner cases. If this goes at least roughly into the direction you want to go, we can certainly massage it to your needs.

General approach:
Instead of modifying the FOV (wouldn’t this lead to distortions?) I use a Python tag to modify just the location of a camera. For now I do this in the most simple way. Instead of going the full length, trying to properly determine the region of the viewport filled by an object, I just use the bounding boxes (currently not even considering their rotation) to calculate a ratio of the object size. Depending on this ratio I move the camera back and forth, so each object roughly has the same perceived size (in the scene below I have placed a few HUD parameters to roughly mark the frame).

Usage:
On frame zero the Python tag is “passive”. It simply watches, what you are doing. Think of it as configuration phase. Move and change the camera in whatever way needed to frame the first object in the way you like. By first object, I mean the first object in the Object Manager, which is not the camera itself.
Now, when you step a frame, the tag takes control of the camera and positions it to the next object (no child objects are considered currently). So, the frame number is currently determining, which object gets framed by the camera. Not a good idea, if you have animation yourself, but I thought for demo purposes this might do.
Jumping back to frame zero, everything should be as before and you are again free to move the camera however you like.

Current most obvious flaw:
The rotation of the object is not correctly considered for the calculation of the size ratio. So with rotated objects with large differences in bounding box proportions this will work sub-optimal, to say the least.

Here you go:
test_autoframecam.c4d (305.0 KB)

Of course this is just one approach.
There are certainly other workflows, e.g. via a script or command plugin.
Other mechanism to determine the objects to frame.
Increased precision.
It should also be possible to completely achieve this with Xpresso without a line of Python.
Less bugs and some thorough thinking to cover my weak mathematics.

By the way, the answer to your actual question is yes :wink:

Cheers

Only for discussion purposes, here’s the code of the Python tag in above scene:

import c4d

ID_LAST_FRAME = 1042 # store the current frame number (in order to detect user jumping back to frame zero)
ID_POS_OFFSET = 1050 # the camera offset set by the user for the first object (on frame zero)

# FRAME_CONFIG is the number of the frame,
# when the tag does not influence the camera.
FRAME_CONFIG = 0  # TODO maybe frame -1 is a better choice?

def main():
    bc = op.GetDataInstance()
    cam = op.GetObject()

    # Determine current frame
    fps = doc.GetFps()
    f = doc.GetTime().GetFrame(fps)

    # Get first object in scene, which is NOT the camera
    objFirst = doc.GetFirstObject()
    if objFirst == cam:
        objFirst = obj.GetNext()
    if objFirst is None:
        return

    objFirstFocus = objFirst.GetAbsPos() + objFirst.GetMp()

    # When returning to frame zero, reset to the state
    if f == 0 and bc[ID_LAST_FRAME] != 0 and bc[ID_POS_OFFSET] is not None:
        cam.SetAbsPos(objFirstFocus - bc[ID_POS_OFFSET])
        #print 'reset'
    bc[ID_LAST_FRAME] = f

    # On frame zero, do nothing.
    # Just let the user potition and configure his camera for the FIRST object
    # and store the information needed to replicate this configuration for
    # other objects.
    if f <= FRAME_CONFIG:
        bc[ID_POS_OFFSET] = objFirstFocus - cam.GetAbsPos()
        #print 'store'
        return

    # Choose object by frame number.
    # lock on last object for higher frame numbers
    # and ignore our camera object
    obj = objFirst
    n = f
    while n > 0:
        objNext = obj.GetNext()
        if objNext is None:
            break
        if objNext == cam:
            if cam.GetNext is not None: # skip camera
                objNext = cam.GetNext()
            else:                
                break
        obj = objNext
        n -= 1

    print "###", obj.GetName()

    # Calculate new camera position for chosen object by positioning
    # the camera with the same offset as the first object and move the
    # camera along its Z-axis by the ratio of the bounding boxes of
    # first and chosen object.
    bbObjFirst = objFirst.GetRad()
    bbObj = obj.GetRad()
    # TODO consider object rotation for bounding box
    ratioX = bbObj.x / bbObjFirst.x
    ratioY = bbObj.y / bbObjFirst.y
    ratioZ = bbObj.z / bbObjFirst.z
    ratioMax = max(ratioX, ratioY, ratioZ)

    # Move camera
    mObjRot = c4d.utils.HPBToMatrix(obj.GetAbsRot())
    objMp = mObjRot * obj.GetMp()
    cam.SetAbsPos(obj.GetAbsPos() + objMp - (bc[ID_POS_OFFSET] * ratioMax))