Animated Tiled Camera



  • On 25/10/2016 at 08:31, xxxxxxxx wrote:

    Hello,

    Got a little project I'm working on.
    Basically I'm trying to make a script that will create a tiled camera for every frame in a user defined range, then save a new file with the frame number as a suffix after setting the tile camera to the scene camera for that frame.

    We start with one animated camera

    I've identified the following main tasks:

    1. From the animated camera, create one static camera for each frame of animation in the user defined range.
    2. Name the cameras with the frame of creation.
    3. Create tiled camera (from c4d content library) and assign the static camera as reference camera.
    4. Set number of tiles per axis.
    5. Enable use tiling.
    6. Set tiled camera as scene camera.
    7. Save file as "documentname_TileFrame####.c4d"

    The settings on the tiled camera:

    The code I've got so far is:

    import c4d  
    from c4d import gui  
    import os  
      
    # Returns a BaseDraw instance  
    bd = doc.GetActiveBaseDraw()  
      
    def set_render_cam() :  
      """  
      Sets the currently selected camera to the active render camera  
      """  
      # Get the selected camera  
      cam = doc.GetActiveObject()  
      # Set the active camera  
      bd.SetSceneCamera(cam)  
      # Re-draws the view  
      c4d.DrawViews()  
      # Adds event  
      c4d.EventAdd()  
      
    def camera_setup() :  
      # Get int of current frame  
      current_frame = doc.GetTime().GetFrame(doc.GetFps())  
       
      # Create null for camera group and set name  
      camgroup = c4d.BaseObject(c4d.Onull)  
      camgroup.InsertBefore(doc.GetFirstObject())  
      camgroup.SetName('CamGroup Frame' + str(current_frame))  
      # Create new cam in null and set name  
      newcam = c4d.BaseObject(c4d.Ocamera)  
      doc.InsertObject(newcam, parent=camgroup)  
      newcam.SetName('Frame ' + str(current_frame))  
       
      # Gets scene cam data and copies to new cam  
      scene_cam = bd.GetSceneCamera(doc)  
      newcam.SetData(scene_cam.GetData())  
      newcam.SetMg(scene_cam.GetMln())    
       
      # Clones Tiled Camera Base  
      tilecambase = doc.SearchObject("Tiled Camera Base")  
      tilecamclone = tilecambase.GetClone()  
      doc.InsertObject(tilecamclone, parent=camgroup)  
      tilecamclone.SetName('Tiled Cam - Frame ' + str(current_frame))  
       
      # Sets 'tiles per axis', 'reference camera' and 'use tiling'  
      tilecamclone[c4d.ID_USERDATA,3] = 5  
      tilecamclone[c4d.ID_USERDATA,4] = doc.SearchObject('Frame ' + str(current_frame))  
      tilecamclone[c4d.ID_USERDATA,1] = 1  
       
      # Set the scene camera to tiled camera  
      ##bd.SetSceneCamera(tilecamclone)  
       
      c4d.EventAdd()  
      c4d.DrawViews()  
       
      
    def cam_range(start, end) :  
      for f in range(start, end + 1) :  
          doc.SetTime(c4d.BaseTime(f, doc.GetFps()))  
          camera_setup()  
       
      c4d.DrawViews()  
      c4d.EventAdd()  
      
      
    def main() :  
      cam_range(91, 100)  
       
      
    if __name__=='__main__':  
      main()  
      
    
    

    When I run it, it creates the cameras for each frame in the range and names them accordingly.
    However, it fails to copy the coordinates (Matrix?) across every frame from the animated camera to each static camera.

    Any ideas on how to achieve this?
    I've tried the usual googling, but come up dry.

    Thanks in advance

    Tom



  • On 26/10/2016 at 02:31, xxxxxxxx wrote:

    I've managed to get it working!

    In case anyone else is having a similar problem looping through frames and copying values, here is my solution:

    In the loop section of my code:

      
    def cam_range(start, end) :  
      for f in range(start, end + 1) :  
          doc.SetTime(c4d.BaseTime(f, doc.GetFps()))  
          camera_setup()          
          c4d.DrawViews()  
          c4d.EventAdd()  
    

    I needed to add a [

    doc.ExecutePasses(None, True, True, True, 0)
    

    ](https://developers.maxon.net/docs/Cinema4DPythonSDK/html/modules/c4d.documents/BaseDocument/index.html?highlight=execute#BaseDocument.ExecutePasses)
    This gets it to evaluate every frame. Not sure what else it does, but my script is now working.

    So this is what my loop code looks like now:

      
    def cam_range(start, end) :  
      for f in range(start, end + 1) :  
          doc.SetTime(c4d.BaseTime(f, doc.GetFps()))  
          doc.ExecutePasses(None, True, True, True, 0)  
          camera_setup()          
          c4d.DrawViews()  
          c4d.EventAdd()  
    

    Cheers!



  • On 26/10/2016 at 06:26, xxxxxxxx wrote:

    Hi Tom,

    welcome to the Plugin Café forums 🙂

    Before suggesting another approach, I'd like to mention a few things in your current code:
    - You are using doc.SetTime(), which is probably not enough for what you want. Additionally you will need to "animate" the document to the given time using ExecutePasses().
    - Then there's an excessive use of EventAdd() and DrawViews() calls. I don't think you need DrawViews() at all in your situation. And EventAdd() is enough to call once after you did a change to the scene. Probably these are just signs of desperation 😉
    - In order to transfer the animation from one object to another, you'd normally copy the CTrack(s).

    Now, just by accident I did a script for the tiled camera some time ago. The idea was to script the functionality of the Tiled Camera, so that Xpresso wasn't needed and I could add a command line option for tiled rendering.
    Here's some of the code, it's just ripped from the middle, but I'm sure you get the point:

    	bd = doc.GetRenderBaseDraw()
    	if bd is None:
    		return
      
    	# Get the active camera
    	cam = bd.GetSceneCamera(doc)
    	if cam is None:
    		return
      
    	# Get the active render settings and output path
    	rd = doc.GetActiveRenderData()
    	filepath, filename = ntpath.split(rd[c4d.RDATA_PATH])
      
    	for tileIdx in range(tileIdxMin, tileIdxMax) :
    		# Prefix the output filename with tile index
    		outputPath = filepath + "/" + str(tileIdx) + "_" + filename
    		rd[c4d.RDATA_PATH] = outputPath
      
    		# Create a new camera and configure it for tiled render
    		camOffset = c4d.BaseObject(c4d.Ocamera)
    		camOffset.SetMg(cam.GetMg())
    		camOffset[c4d.CAMERA_PROJECTION] = cam[c4d.CAMERA_PROJECTION]
    		camOffset[c4d.CAMERAOBJECT_APERTURE] = cam[c4d.CAMERAOBJECT_APERTURE]
    		camOffset[c4d.CAMERAOBJECT_SHOW] = cam[c4d.CAMERAOBJECT_SHOW]
    		camOffset[c4d.CAMERA_FOCUS] = cam[c4d.CAMERA_FOCUS] * fNumTiles
    		camOffset[c4d.CAMERA_ZOOM] = cam[c4d.CAMERA_ZOOM] * fNumTiles
      
    		biasX = math.fmod(float(tileIdx), fNumTiles) - (fNumTiles - 1.0) * 0.5
    		biasY = float(tileIdx / numTiles) - (fNumTiles - 1.0) * 0.5
      
    		camOffset[c4d.CAMERAOBJECT_FILM_OFFSET_X] = fNumTiles * cam[c4d.CAMERAOBJECT_FILM_OFFSET_X] + biasX
    		camOffset[c4d.CAMERAOBJECT_FILM_OFFSET_Y] = fNumTiles * cam[c4d.CAMERAOBJECT_FILM_OFFSET_Y] + biasY
      
    		# Insert the new camera into the scene and set it for rendering
    		doc.InsertObject(camOffset)
    		bd.SetSceneCamera(camOffset)
      
    		# Initialize a bitmap with the result size (must match output size of render settings)
    		# ...
    		# Render the file (and assure saving of rendered image)
    		# ...
    

    So, why am I posting this code? Well, basically with this, you can have a tiled camera, which is independent of animation (opposed to the one in Content Browser). And you don't need to set up one per frame. Instead you can have just one tiled camera (or maybe number of tiles tiled cameras), which you could simply make child of your animated camera. And without copying any animation, you could just render these cameras over the desired frame range. Just an idea.


Log in to reply