Maybe this script helps you to frame your objects manually. The called helper methods should be self-explanatory.
If you have any questions, feel free to ask.
import c4d
import sys
import math
from .. import Helpers
class FramedCamera(object):
"""
Modifies a given camera to frame a given object.
The object to frame must be a clone since it is modified
"""
def __init__(self, frame_obj, camera, azimuth=00, elevation=0):
self.frame_object(frame_obj, camera, azimuth, elevation)
# calculate the bounding sphere based on the camera FOV
def calc_bsphere(self, op, camera):
"""Calculates a bounding sphere from a given object considering the FOV
Args:
op (c4d.BaseObject): The object to calculate the bounding sphere from
camera (c4d.BaseObject): The camera to get the FOV from
Returns:
list: The bounding sphere center and radius
"""
points = Helpers.GetAllPoints(op)
if not points:
raise ValueError("Object {} has no points".format(op.GetName()))
p_min = c4d.Vector(sys.float_info.max)
p_max = c4d.Vector(-sys.float_info.max)
for p in points:
p_min = c4d.Vector(min(p.x, p_min.x), min(p.y, p_min.y), min(p.z, p_min.z))
p_max = c4d.Vector(max(p.x, p_max.x), max(p.y, p_max.y), max(p.z, p_max.z))
center = (p_min + p_max) * 0.5
radius = 0
for p in points:
radius = max(radius, (center - p).GetLength())
fov_h = camera[c4d.CAMERAOBJECT_FOV]
fov_v = camera[c4d.CAMERAOBJECT_FOV_VERTICAL]
radius = (radius * 1.1) / math.sin(max(fov_h, fov_v) * 0.5)
return center, radius
# LookAt function
def look_at(self, origin, target):
"""
Creates an orientation matrix from two vectors
Args:
origin (c4d.Vector): The Vector to be used to orient
target (c4d.Vector): The target vector
Returns:
c4d.Matrix: The orientation matrix
"""
mat = c4d.Matrix()
up_temp = c4d.Vector(0, 1.0, 0)
v_fwd = (target - origin).GetNormalized()
v_right = up_temp.Cross(v_fwd).GetNormalized()
v_up = v_fwd.Cross(v_right).GetNormalized()
mat.off = origin
mat.v1 = v_right
mat.v2 = v_up
mat.v3 = v_fwd
return mat
# frame objects
def frame_object(self, op, camera, elevation, azimuth):
"""
Places and orients a camera to fit a given object An optional angle (H,P) can be given.
Args:
op (c4d.BaseObject): The object to frame
camera (c4d.BaseObject): The camera that is used
elevation (float): Camera Heading, in radians
azimuth (float): Camera Pitch, in radians
Returns:
bool: True for success, False otherwise.
"""
Helpers.convertInstances(op)
# Get bounding sphere depending on camera FOV
b_sphere = self.calc_bsphere(op, camera)
if not b_sphere:
raise ValueError("Bounding sphere cannot be calculated")
# Set camera position and direction
center, radius = b_sphere
camera.SetMg(c4d.Matrix())
cam_pos = c4d.Vector(center)
cam_pos.x = cam_pos.x + math.cos(azimuth) * math.sin(elevation) * radius
cam_pos.y = cam_pos.y + math.sin(azimuth) * radius
cam_pos.z = cam_pos.z + math.cos(azimuth) * math.cos(elevation) * -radius
camera.SetMg(self.look_at(cam_pos, center))
Side note. This code comes from a prototype, therefore it is not optimized/comes with some syntactical flaws.