Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. Futurium
    F
    • Profile
    • Following 0
    • Followers 1
    • Topics 10
    • Posts 30
    • Best 0
    • Controversial 0
    • Groups 0

    Futurium

    @Futurium

    0
    Reputation
    14
    Profile views
    30
    Posts
    1
    Followers
    0
    Following
    Joined Last Online

    Futurium Unfollow Follow

    Latest posts made by Futurium

    • Capturing and Restoring Take Overrides in Cinema 4D Using JSON

      Hi,
      I'm working on a code to copy take overrides from one scene to another. One script captures details from each take in my reference scene and saves them as a JSON file. Another script loads the JSON and compares all takes to identify differences in names, hierarchy, applied materials, transforms, visibility, etc.

      I want the script to both print the differences between the captured JSON data and the current scene and automatically apply the take-specific changes. I assume the take hierarchy, scene objects, and materials are identical (which I verify elsewhere).

      I can capture many details but struggle to compare takes with the JSON data and restore them correctly. I also suspect my code is unnecessarily long and complex for such a simple task. Could you review both my capture and restore scripts and help me get them working efficiently?

      I've attached my test scenes and scripts.
      Thank you in advance.

      Best regards,
      Tomasz

      #GrabReferenceSnapshot.py
      import c4d
      import collections
      import maxon
      from c4d import utils
      import json
      import os
      
      def create_snapshot(doc, filepath):
          def return_full_path_per_object(op, parent_mesh_object_name=""):
              if not op:
                  return ""
              
              path_parts = []
              current = op
              
              # Build path from current object up to root
              while current:
                  name = current.GetName()
                  if name == parent_mesh_object_name:
                      break
                  path_parts.insert(0, name)
                  current = current.GetUp()
              
              # Join path parts with forward slashes
              return "/".join(path_parts)
          
          def serialize_descid(desc_id):
              """Converts a c4d.DescID to a serializable format."""
              return [desc_id[i].id for i in range(desc_id.GetDepth())]
      
          try:
              # Ensure the main take is active
              take_data = doc.GetTakeData()
              if take_data is None:
                  raise RuntimeError("No take data in the document.")
              main_take = take_data.GetMainTake()
              take_data.SetCurrentTake(main_take)
              
              # Initialize the snapshot dictionary
              snapshot = {
                  'takes': {}
              }
              
              # Get takes information
              take_data = doc.GetTakeData()
              if take_data:
                  main_take = take_data.GetMainTake()
                  
                  def process_take_changes(take):
                      """Gets the overrides for a specific take."""
                      if take == main_take:
                          return {"name": take.GetName(), "overrides": []}  # Skip overrides for main take
                      
                      take_data.SetCurrentTake(take)
                      doc = c4d.documents.GetActiveDocument()
                      doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_0)
                      
                      take_changes = {
                          "name": take.GetName(),
                          "overrides": []
                      }
                      
                      # Get only the overrides for this take
                      override_data = take.GetOverrides()
                      if override_data:
                          for override in override_data:
                              override_obj = override.GetSceneNode()
                              if not override_obj:
                                  continue
                                  
                              # Log the object being processed
                              print(f"Processing override for object: {override_obj.GetName()}")
                              
                              # Use the material name as a unique identifier
                              material_name = override_obj.GetName() if isinstance(override_obj, c4d.BaseMaterial) else None
                              
                              overridden_params = override.GetAllOverrideDescID()
                              for desc_id in overridden_params:
                                  description = override_obj.GetDescription(c4d.DESCFLAGS_DESC_NONE)
                                  if description:
                                      parameter_container = description.GetParameter(desc_id, None)
                                      if parameter_container:
                                          param_name = parameter_container[c4d.DESC_NAME]
                                          value = override.GetParameter(desc_id, c4d.DESCFLAGS_GET_0)
                                          
                                          # Use return_full_path_per_object to get the object path
                                          print (f"in take{take.GetName()} checking object path for {override_obj.GetName()}")
                                          object_path = return_full_path_per_object(override_obj, "")
                                          if "/" in object_path:
                                              print (f"object_path: {object_path}")
                                          
                                          take_changes["overrides"].append({
                                              "object_name": override_obj.GetName(),
                                              "object_path": object_path,
                                              "parameter": param_name,
                                              "parameter_id": serialize_descid(desc_id),
                                              "value": str(value) if not isinstance(value, (int, float, bool, str)) else value,
                                              "material_name": material_name  # Add material name for comparison
                                          })
                                          
                                          print(f"Captured override: {param_name} = {value} for object {object_path}")
                      
                      return take_changes
      
                  def process_take(take):
                      if not take:
                          return
                      
                      # Store only serializable data
                      take_info = {
                          'name': take.GetName(),
                          'parent': take.GetUp().GetName() if take.GetUp() else None,
                          'changes': process_take_changes(take)  # Capture take changes
                      }
                      
                      # Store in snapshot using take name as key
                      snapshot['takes'][take.GetName()] = take_info
                      
                      # Process child takes
                      child = take.GetDown()
                      while child:
                          process_take(child)
                          child = child.GetNext()
                  
                  process_take(main_take)
              
              # Save the snapshot to JSON file
              try:
                  # Create all necessary directories
                  os.makedirs(os.path.dirname(filepath), exist_ok=True)
                  
                  # Save the file
                  with open(filepath, 'w', encoding='utf-8') as f:
                      json.dump(snapshot, f, indent=4)
                      
                  return True
                  
              except Exception as e:
                  print(f"Error saving snapshot file: {str(e)}")
                  return False
              
          except Exception as e:
              print(f"Error creating snapshot: {str(e)}")
              return False
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          
          # Get both the document name and path
          doc_name = doc.GetDocumentName()
          doc_path = doc.GetDocumentPath()
          
          full_path = os.path.join(doc_path, doc_name)
          print("full_path: " + str(full_path))
          
          project_folder = os.path.dirname(full_path)
          print("project_folder : " + str(project_folder))
          
          project_name = os.path.basename(project_folder)
          print("project_name : " + str(project_name))
      
          # Construct final path
          final_path = os.path.join(project_folder, "snapshots", project_name + "_final.json")
          
          print("\nPath verification:")
          print(f"Final snapshot path: {final_path}")
          
          print("\nAttempting to create final snapshot...")
          try:
              result = create_snapshot(doc, final_path)
              print(f"create_snapshot call completed. Result: {result}")
          except Exception as e:
              print(f"Error in create_snapshot: {str(e)}")
              import traceback
              print(f"Traceback:\n{traceback.format_exc()}")
      
      if __name__ == "__main__":
          c4d.CallCommand(13957) # Clear Console
          main()
      
      #RestoreSnapshot
      import c4d
      import collections
      import maxon
      from c4d import utils
      import os
      import c4d
      import json
      
      def return_full_path_per_object(op, parent_mesh_object_name=""):
          if not op:
              return ""
          
          path_parts = []
          current = op
          
          # Build path from current object up to root
          while current:
              name = current.GetName()
              if name == parent_mesh_object_name:
                  break
              path_parts.insert(0, name)
              current = current.GetUp()
          
          # Join path parts with forward slashes
          return "/".join(path_parts)
      
      def serialize_descid(desc_id):
          """Converts a c4d.DescID to a serializable format."""
          return [desc_id[i].id for i in range(desc_id.GetDepth())]
      
      def check_takes(doc, snapshot):
          
          try:
              print("\nPerforming takes check...")
              
              take_data = doc.GetTakeData()
              if take_data is None:
                  raise RuntimeError("No take data in the document.")
                  
              main_take = take_data.GetMainTake()
              snapshot_takes = snapshot.get('takes', {})
              
              def get_take_overrides(take):
                  """Gets the overrides for a specific take."""
                  if take == main_take:
                      return []  # Skip overrides for main take
                  
                  # Activate the take before checking its overrides
                  take_data.SetCurrentTake(take)
                  doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_0)
                  
                  override_data = take.GetOverrides()
                  overrides = []
                  
                  if override_data:
                      for override in override_data:
                          override_obj = override.GetSceneNode()
                          if not override_obj:
                              continue
                              
                          overridden_params = override.GetAllOverrideDescID()
                          for desc_id in overridden_params:
                              description = override_obj.GetDescription(c4d.DESCFLAGS_DESC_NONE)
                              if description:
                                  parameter_container = description.GetParameter(desc_id, None)
                                  if parameter_container:
                                      param_name = parameter_container[c4d.DESC_NAME]
                                      value = override.GetParameter(desc_id, c4d.DESCFLAGS_GET_0)
                                      
                                      # Get object path using the helper function
                                      object_path = return_full_path_per_object(override_obj)
                                      
                                      # Use the material name or another unique identifier
                                      material_name = override_obj.GetName() if isinstance(override_obj, c4d.BaseMaterial) else None
                                      
                                      overrides.append({
                                          "object_name": override_obj.GetName(),
                                          "object_path": object_path,
                                          "parameter": param_name,
                                          "parameter_id": str(desc_id),  # Convert DescID to string for comparison
                                          "value": str(value) if not isinstance(value, (int, float, bool, str)) else value,
                                          "material_name": material_name  # Add material name for comparison
                                      })
                  
                  return overrides
              
              def compare_takes(current_take, snapshot_take_data):
                  # Debugging: Print snapshot_take_data to verify its structure
                  print(f"Snapshot data for take '{current_take.GetName()}': {snapshot_take_data}")
      
                  # Ensure 'overrides' key exists in snapshot_take_data
                  if 'overrides' not in snapshot_take_data:
                      print(f"Error: 'overrides' key not found in snapshot data for take '{current_take.GetName()}'")
                      return False
      
                  # Initialize current_overrides with the current scene's override data
                  current_overrides = []  # Ensure this is defined before use
      
                  # Retrieve the current overrides for the take
                  override_data = current_take.GetOverrides()
                  if override_data:
                      for override in override_data:
                          override_obj = override.GetSceneNode()
                          if not override_obj:
                              continue
      
                          # Use the material name as a unique identifier
                          material_name = override_obj.GetName() if isinstance(override_obj, c4d.BaseMaterial) else None
      
                          overridden_params = override.GetAllOverrideDescID()
                          for desc_id in overridden_params:
                              description = override_obj.GetDescription(c4d.DESCFLAGS_DESC_NONE)
                              if description:
                                  parameter_container = description.GetParameter(desc_id, None)
                                  if parameter_container:
                                      param_name = parameter_container[c4d.DESC_NAME]
                                      value = override.GetParameter(desc_id, c4d.DESCFLAGS_GET_0)
      
                                      # Use return_full_path_per_object to get the object path
                                      object_path = return_full_path_per_object(override_obj, "")
      
                                      current_overrides.append({
                                          "object_name": override_obj.GetName(),
                                          "object_path": object_path,
                                          "parameter": param_name,
                                          "parameter_id": serialize_descid(desc_id),
                                          "value": str(value) if not isinstance(value, (int, float, bool, str)) else value,
                                          "material_name": material_name  # Add material name for comparison
                                      })
      
                  # Now compare current_overrides with snapshot_take_data['overrides']
                  snapshot_overrides = snapshot_take_data['overrides']
      
                  for curr_override, snap_override in zip(current_overrides, snapshot_overrides):
                      # Compare using material names if available
                      if curr_override.get('material_name') != snap_override.get('material_name'):
                          print(f"  Material name mismatch in take '{current_take.GetName()}':")
                          print(f"  Expected: {snap_override.get('material_name')}")
                          print(f"  Found: {curr_override.get('material_name')}")
                          return False
      
                      # Compare other attributes
                      if (curr_override['object_path'] != snap_override['object_path'] or
                          curr_override['parameter'] != snap_override['parameter'] or
                          curr_override['value'] != snap_override['value']):
                          print(f"  Override mismatch in take '{current_take.GetName()}':")
                          print(f"  Object: {curr_override['object_path']}")
                          print(f"  Parameter: {curr_override['parameter']}")
                          print(f"  Expected: {snap_override['value']}")
                          print(f"  Found: {curr_override['value']}")
                          return False
      
                  return True
              
              # Start comparison with main take
              all_match = True
              
              def process_take(take):
                  nonlocal all_match
                  if not take:
                      return
                  
                  take_name = take.GetName()
                  if take_name not in snapshot_takes:
                      print(f"\nTake not found in snapshot: {take_name}")
                      all_match = False
                      return
                  
                  # Compare current take with snapshot
                  if not compare_takes(take, snapshot_takes[take_name]):
                      all_match = False
                  
                  # Process child takes
                  child = take.GetDown()
                  while child:
                      process_take(child)
                      child = child.GetNext()
              
              try:
                  # Start with main take
                  process_take(main_take)
                  
                  # Restore main take at the end
                  take_data.SetCurrentTake(main_take)
                  doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_0)
                  
                  if all_match:
                      print("\nAll takes match.")
                  else:
                      print("\nTakes check failed.")
                  
                  return all_match
                  
              except Exception as e:
                  print(f"Error during take comparison: {str(e)}")
                  # Ensure main take is restored even if an error occurs
                  take_data.SetCurrentTake(main_take)
                  doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_0)
                  return False
              
          except Exception as e:
              print(f"Error checking takes: {str(e)}")
              return False
      
      def restore_snapshot(doc, snapshot_path, loose_comparison=False):
      
          try:
              print("Loading snapshot file...")
              # Load the snapshot file
              with open(snapshot_path, 'r', encoding='utf-8') as f:
                  snapshot = json.load(f)
              
              # Ensure the main take is active
              take_data = doc.GetTakeData()
              if take_data is None:
                  raise RuntimeError("No take data in the document.")
              main_take = take_data.GetMainTake()
              take_data.SetCurrentTake(main_take)
                      
              # Check takes
              if not check_takes(doc, snapshot):
                  return False
              
              print("Snapshot restoration check completed successfully.")
              return True
              
          except Exception as e:
              print(f"Error checking snapshot: {str(e)}")
              return False
      
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          
          # Get both the document name and path
          doc_name = doc.GetDocumentName()
          doc_path = doc.GetDocumentPath()
          
          full_path = os.path.join(doc_path, doc_name)
          print("full_path: " + str(full_path))
          
          project_folder = os.path.dirname(full_path)
          print("project_folder : " + str(project_folder))
          
          project_name = os.path.basename(project_folder)
          print("project_name : " + str(project_name))
      
          # Construct path to the snapshot we want to restore
          snapshot_path = os.path.join(project_folder, "snapshots", project_name + "_final.json")
          
          print("\nPath verification:")
          print(f"Snapshot path to restore: {snapshot_path}")
          
          # Verify the snapshot file exists
          if not os.path.exists(snapshot_path):
              print(f"Error: Snapshot file not found at {snapshot_path}")
              return
          
          print("\nAttempting to restore snapshot...")
          try:
              result = restore_snapshot(doc, snapshot_path)
              print(f"restore_snapshot call completed. Result: {result}")
          except Exception as e:
              print(f"Error in restore_snapshot: {str(e)}")
              import traceback
              print(f"Traceback:\n{traceback.format_exc()}")
      
      if __name__ == "__main__":
          c4d.CallCommand(13957) # Clear Console
          main()
      

      Scenes for testing:
      SimpleScene4Reference.c4d
      SimpleScene4Restore.c4d

      posted in Cinema 4D SDK python 2023
      F
      Futurium
    • Texture Baking in Redshift using Python

      Hi,
      I have a simple scene with a few takes, each using a different material, and two baking sets. I’m trying to write a script that travels through the takes, accesses the corresponding baking set, bakes the textures for each take, and then moves to the next one. While I’ve written some initial code, I couldn’t find any documentation or examples specific to Redshift to help me make it work.

      Could you please review my attached code and sample scene? If possible, could you provide a code snippet demonstrating how to implement this functionality?

      I also have a couple of related questions:

      1. Is there a way to increase baked texture brightness without altering light intensity or modifying the materials? I’d like to match the renders , where I manipulate brightness using f-Stop on Redshift camera. I understand that exposure manipulation may not work during baking, but any suggestions would be helpful.
      2. All my AOVs are set up in Redshift render settings. Can they be used during the baking process?
      3. When baking textures, Redshift materials using standard bluish normal maps result in green normals. Is there a way to resolve this issue?

      Thank you for your assistance. I’ve attached my initial code and a sample scene for your reference.

      Best regards,
      Tomasz

      MatBaker_SImplified.c4d

      import c4d
      import re
      
      list_of_takes = []
      cat_2_baker = {"RS BakeSet_WallTiles":"003","RS BakeSet_FeatureWalls":"002"}
      
      def get_all_takes_recursively(take, takeData):
          while take:
              if "_BakeMat_" in take.GetName():
                  list_of_takes.append(take)
                  #print(f"Added {take.GetName()}")
              get_all_takes_recursively(take.GetDown(), takeData)
              take = take.GetNext()
      
      def node_2_bakerset(take_name):
          match = re.search(r"_BakeMat_(\d{3})", take_name)
          if match:
              code = match.group(1)
              result = next((key for key, value in cat_2_baker.items() if value == code), None)
              return result
          else:
              return None
      
      def bake_textures(take, bakingset_2_use):
          print (f"Baking mat {take.GetName()} using {bakingset_2_use} set")
      
      def main():
      
          doc = c4d.documents.GetActiveDocument()
          if not doc:
              return
      
          # Get take data
          take_data = doc.GetTakeData()
          if not take_data:
              print("No take data in document")
      
          get_all_takes_recursively(take_data.GetMainTake(), take_data)
      
          for take in list_of_takes:
              #print(take.GetName())
              node_2_use = node_2_bakerset(take.GetName())
              bake_textures(take, node_2_use)
      
          # Create parent take for material previews
          c4d.EventAdd()
      
      if __name__ == '__main__':
          c4d.CallCommand(13957)  # Clear Console
          main()
      
      posted in Cinema 4D SDK python 2023
      F
      Futurium
    • RE: List of object visible by a camera within Safe Frames

      Hi @i_mazlov ,
      Thank you for confirming my solution.
      Best regards,
      Tomasz

      posted in Cinema 4D SDK
      F
      Futurium
    • RE: List of object visible by a camera within Safe Frames

      Hi @mogh,
      Thank you for your suggestion. Currently, I've developed a method where I replace a spherical camera view with a standard camera, capture what's visible, and rotate until I've covered the entire area. This approach meets my goals, although I'm still assessing its efficiency. Your idea of rendering the image and selecting from it sounds like another option and might be a good alternative.

      posted in Cinema 4D SDK
      F
      Futurium
    • RE: Troubleshooting Safe Frame Calculations Across Different Takes in Cinema 4D

      Thank you @i_mazlov

      It works perfectly 🙂

      Best regards,
      Tomasz

      posted in Cinema 4D SDK
      F
      Futurium
    • RE: List of object visible by a camera within Safe Frames

      Hi @i_mazlov ,

      I'm writing to follow up on my previous message about the issue with 360 cameras in my code as I haven't heard back. Could you please provide any updates or further guidance?

      Best regards,
      Tomasz

      posted in Cinema 4D SDK
      F
      Futurium
    • Troubleshooting Safe Frame Calculations Across Different Takes in Cinema 4D

      Hi,

      I want to calculate the safe frame value for all takes, where each take has a different camera and possibly different render settings. If I manually change a take and run my code, it returns the same safe frame for all other takes as for the currently selected one, which is incorrect in my case. It seems the values used for safe frame calculations are not being updated when I iterate through takes via code. What am I missing? I've attached a simple test scene to test the code.

      Best regards,
      Tomasz

      import c4d
      
      def grab_children_of_main_take(take_data):
          children = []
          mainTake = take_data.GetMainTake()
          take = mainTake.GetDown()
      
          while take is not None:
              children.append(take)
              take = take.GetNext()
      
          return children
      
      def get_safe_frame_for_the_current_per_take(doc):
          bd = doc.GetRenderBaseDraw()
          safeframe = bd.GetSafeFrame()
          print(safeframe)
      
      
      def main():
          c4d.CallCommand(13957)  # Clear Console
          doc = c4d.documents.GetActiveDocument()
          take_data = doc.GetTakeData()
          if take_data is None:
              raise RuntimeError("Failed to retrieve the take data.")
      
          children_of_main_take = grab_children_of_main_take(take_data)
          print("Returned : " + str(len(children_of_main_take)) + " children of main take")
      
          for child_take in children_of_main_take:
              take_data.SetCurrentTake(child_take)
              doc.ExecutePasses(bt=None, animation=True, expressions=True, caches=True, flags=c4d.BUILDFLAGS_NONE)
              current_camera = child_take.GetCamera(take_data)
              print("\n--------------------------------------------------")
              print(f"Checking take {child_take.GetName()} with camera {current_camera.GetName()}")
              get_safe_frame_for_the_current_per_take(doc)
      
      if __name__ == '__main__':
          c4d.CallCommand(13957)  # Clear Console
          main()
      

      SafeFrameWithTakes.c4d

      posted in Cinema 4D SDK python
      F
      Futurium
    • RE: Speed up rendering by transforming takes into frames

      Hello @ferdinand

      Thank you for your guidance. I'll follow your suggestion and reach out to the support team for assistance with my query.

      Best regards,
      Tomasz

      posted in General Talk
      F
      Futurium
    • Speed up rendering by transforming takes into frames

      Hi,
      One solution we offer our customers requires several thousand takes for each set of renders. Each take only needs 30 seconds for rendering, but the preparation phase, which involves processing geometry with Redshift, is much longer. On average, we use one camera for every 500 takes, and the only changes between takes are material swapping and toggling some geometry's visibility. I've developed a Python script that logs changes to takes and creates an animation frame for each, significantly speeding up the rendering process when rendering as an animation compared to individual takes. Is there a way to achieve this increased rendering speed with takes without converting them into animation frames? Do you have any other suggestions for enhancing render speed?

      Best regards,
      Tomasz

      posted in General Talk windows s26
      F
      Futurium
    • RE: List of object visible by a camera within Safe Frames

      Hi Ilia,

      Thank you for guiding me in the right direction. I've revised my code following your advice, and it's working well with standard cameras. However, it's having difficulty with 360 cameras. The code successfully identifies larger objects but struggles to detect smaller ones, even after I increased the accuracy (step=1, radius=1). Do you have any suggestions?

      Best regards,
      Tomasz

      posted in Cinema 4D SDK
      F
      Futurium