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
    • Register
    • Login
    1. Home
    2. ll2pakll
    L
    • Profile
    • Following 0
    • Followers 0
    • Topics 8
    • Posts 17
    • Best 2
    • Controversial 0
    • Groups 0

    ll2pakll

    @ll2pakll

    2
    Reputation
    7
    Profile views
    17
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    ll2pakll Unfollow Follow

    Best posts made by ll2pakll

    • RE: Python Xpresso Node

      @m_adam Thank you very much for your reply. But while I was waiting for a reply, I experimented and realized that the Python tag suits me much better than the xpresso system, and rewrote the whole scene overnight). Have a great day.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • RE: Python tag doesn't work when rendering

      @ferdinand Your solution of replacing the doc = c4d.documents.GetActiveDocument() line with doc: c4d.documents.BaseDocument = op.GetDocument() really worked. From my point of view, as an ordinary person, your level of understanding of the code seems to be something beyond the realm of possibility. I wish you a good day and thank you again for your help.

      posted in Cinema 4D SDK
      L
      ll2pakll

    Latest posts made by ll2pakll

    • The cloner is calculated before the Python tag

      Good afternoon, friends. I have a spline formula in my scene, which is used as an object with clones on its vertices. This spline is modified by a formula which is regulated by an expression in a python tag.

      from typing import Optional
      import c4d
      import math
      def main() -> None:
          doc: c4d.documents.BaseDocument = op.GetDocument()
          wave_obj = doc.SearchObject('Wave 1')
      
          wave_obj[c4d.PRIM_FORMULA_TMAX] = 33
          velocity_factor = doc.GetTime().Get() * 100
          wave_obj[c4d.PRIM_FORMULA_Y] = f"{str(20)}*Sin((t-{str(math.radians(velocity_factor))})*{str(0.8)})"
          # Update the scene
          c4d.EventAdd()
      

      The problem is that the cloner executes before the expression, and for this reason, the clones are placed not on the current vertices of the spline, but on the positions of vertices from the previous frame. If you stop the animation, then at the next update of the scene clones become on the right places, but during the animation they still lag a frame. I've tried changing this by decreasing or increasing the tag priority, but it has no effect. How can I solve this problem?

      posted in Cinema 4D SDK 2023 python
      L
      ll2pakll
    • RE: How do you manipulate the points on a spline?

      I found the answer on my own. It turned out that the methods of point management are in the parent class c4d.SplineObject - c4d.PointObject. It is also necessary to call the .Message(c4d.MSG_UPDATE) method, otherwise the spline is not updated.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • How do you manipulate the points on a spline?

      Greetings. Is there any way to manage specific points on a spline? Create, move and delete them?
      I have created a spline with a certain shape, and I want it to be visible not the whole spline, but only a part of it. I want to change the end visible points over time, which will make it look like it is moving in space. I have done this with a "formula", it has parameters for the endpoints and I can change them over time. But using the formula I can't create a spline of the shape I want, and if I convert the formula to a spline and edit it, I find that I can't implement the same movement logic, because I don't understand how I can access the endpoints to create, delete and control their parameters.

      It's a basic script. The second script must be in the file located at the address specified in line 171. It contains the logic of Python tags.

      import c4d
      import math
      import sys
      
      doc: c4d.documents.BaseDocument
      
      
      def open_script_for_python_tag(file_path: str, replacement_dictionary: dict = None):
          """
          Эта функция возвращает открытый файл в котором заменены указанные в replacement_dictionary строки.
      
          :param file_path: путь к файлу
          :param replacement_dictionary: передаётся словарь в котором ключ это строка которую нужно заменить, а значение это
          строка на которую нужно заменить.
          :return:
          """
          # Открываем файл для чтения
          if replacement_dictionary:
              with open(file_path, "r", encoding="utf8") as file:
                  # Читаем все строки файла в список lines
                  lines = file.readlines()
              # Создаем пустую строку result
              result = ""
              # Проходим по каждой строке в списке lines
              for line in lines:
                  for sours, target in replacement_dictionary.items():
                      # Если строка совпадает с ключом, то она заменяется на значение ключа
                      if line == sours:
                          result += target
                      # Иначе оставляем строку без изменений
                      else:
                          result += line
              # Возвращаем результат
              return result
          with open(file_path, "r", encoding="utf8") as file:
              text = file.read()
          return text
      
      
      def get_all_objects() -> list[c4d.BaseObject]:
          """
          # функция, которая возвращает все объекты в сцене.
          :return:
          """
          # Создаем пустой список для хранения имен объектов
          objects = []
          # Получаем корневой объект сцены
          root = doc.GetFirstObject()
          # Если сцена не пуста
          if root:
              # Создаем стек для обхода дерева объектов
              stack = [root]
              # Пока стек не пуст
              while stack:
                  # Извлекаем верхний элемент стека
                  obj = stack.pop()
                  # Добавляем его имя в список
                  objects.append(obj)
                  # Если у объекта есть дочерние объекты
                  if obj.GetDown():
                      # Добавляем их в стек
                      stack.append(obj.GetDown())
                  # Если у объекта есть соседние объекты
                  if obj.GetNext():
                      # Добавляем их в стек
                      stack.append(obj.GetNext())
          # Возвращаем список имен объектов
          return objects
      
      
      def get_all_objects_names():
          """
          Получает имена всех объектов в сцене.
          :return:
          """
          object_names = []
          for object_c4d in get_all_objects():
              object_names.append(object_c4d.GetName())
      
          return object_names
      
      
      def find_all_single_waves() -> list:
          """
          Ищет все объекты одиночных волны в сцене.
          :return:
          """
          # Get the first object in the scene
          obj = doc.GetFirstObject()
          waves = []
          # Loop through all objects in the scene
          while obj:
              # Get the object name
              name = obj.GetName()
      
              # Check if the name matches the pattern "Wave" followed by a number
              if name.startswith("Wave") and name[5:].isdigit():
                  # Print the object name and its global position
                  waves.append(obj)
      
              # Get the next object in the scene
              obj = obj.GetNext()
          return waves
      
      
      class WaveMove:
          def __init__(self, length: (float, int) = 304, amplitude: (float, int) = 100, velocity: (float, int) = 30,
                       phase: int = 0, name: str = None):
              self.__length = length
              self.__amplitude = amplitude
              self.__velocity = velocity
              self.__phase = phase  # в градусах от 0 до 360
              self.__start_point = 0
              self.__end_point = 10
              self._python_tag = None
      
              # создаём сплайн формула, который будет представлять волну
              self.wave_obj = c4d.BaseObject(c4d.Osplineformula)
              # если было передано имя волны то сразу называем её указанным именем
              if name:
                  self.wave_obj.SetName(name)
              else:
                  # если имя не было передано, то смотрим сколько волн в сцене и исходя из этого нумеруем волну.
                  count_waves_in_scene = len(find_all_single_waves())
                  self.wave_obj.SetName('Wave ' + str(count_waves_in_scene))
      
              # меняем плоскость расположения волны
              self.wave_obj[c4d.PRIM_PLANE] = c4d.PRIM_PLANE_ZY
      
              if name == 'Result wave':
                  self.wave_obj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = c4d.OBJECT_OFF
                  self.wave_obj[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = c4d.OBJECT_OFF
              # добавляем волну в сцену
              doc.InsertObject(self.wave_obj)
      
              # создаём пользовательские данные которые будут определять параметры волны
              # создаём дневник с названиями и значениями полей пользовательских данных
              self.ud_dict = {"Length wave": self.__length,
                              "Amplitude wave": self.__amplitude,
                              "Velocity wave": self.__velocity,
                              "Phase wave": self.__phase}
      
              for ud_name, ud_value in self.ud_dict.items():
                  self.create_user_data(ud_name, ud_value)
      
              # устанавливаем точки начала и конца волны
              self.set_start_point(self.__start_point)
              self.set_end_point(self.__end_point)
      
              # устанавливаем количество точек в сплайне
              self.wave_obj[c4d.PRIM_FORMULA_SAMPLES] = 100
      
              # включаем кубическую интерполяцию
              self.wave_obj[c4d.PRIM_FORMULA_CUBIC] = True
      
              # промежуточные точки = равномерно
              self.wave_obj[c4d.SPLINEOBJECT_INTERPOLATION] = c4d.SPLINEOBJECT_INTERPOLATION_UNIFORM
      
              # задаём формулу для поля X(t) которое будет определять длину волны
              self.formula_xt = f"100*t"
              self.set_Xt(self.formula_xt)
      
              # задаём формулу для поля Y(t) которое будет определять амплитуду волны
              self.formula_yt = f"{str(self.__amplitude)}*Sin((t+{str(math.radians(phase))})*{str(self.__length)})"
              self.set_Yt(self.formula_yt)
      
              # создаём словарь в котором будут содержаться строки, которые нужно заменить в скрипте тега
              self.replacement_dictionary = {
                  "    wave_obj: c4d.BaseObject =\n": f"    wave_obj: c4d.BaseObject = doc.SearchObject('{self.wave_obj.GetName()}')\n"
              }
              self.script_for_tag_path = r'F:\Work_area\YouTube\c4d\scripts\9_script_for_teg_single_wave.py'
              self.create_tag_python()
              # self.create_volume()
      
          def create_user_data(self, name: str, value: int):
              """
              Создаёт параметр в пользовательских данных
              :param value:
              :param name:
              :return:
              """
              ud = c4d.GetCustomDataTypeDefault(c4d.DTYPE_LONG)
              ud[c4d.DESC_NAME] = name
      
              self.wave_obj[self.wave_obj.AddUserData(ud)] = value
      
          def create_tag_python(self):
              """
              Создаёт тег Python в котором будет описываться поведение волны и надевает его на объект формула сплайн волны.
              :return:
              """
              # создаём тег и вешаем его
              self._python_tag: c4d.BaseTag = self.wave_obj.MakeTag(c4d.Tpython)
              # помещаем скрипт в тело тега Python и передаём в него вводные данные
              self._python_tag[c4d.TPYTHON_CODE] = open_script_for_python_tag(
                  replacement_dictionary=self.replacement_dictionary,
                  file_path=self.script_for_tag_path
              )
      
          def create_volume(self):
              """
              Создаёт sweep и circle что бы появился объём.
              :return:
              """
              sweep = c4d.BaseObject(c4d.Osweep)
              sweep.SetName(f"Sweep {self.get_name()}")
      
              # создаём окружность
              circle = c4d.BaseObject(c4d.Osplinecircle)
              circle[c4d.PRIM_CIRCLE_RADIUS] = 4
              circle[c4d.SPLINEOBJECT_SUB] = 2
              circle.SetName(f'Circle {self.get_name()}')
      
              # вставляем пружину и спираль в sweep
              self.wave_obj.InsertUnder(sweep)
              circle.InsertUnder(sweep)
              doc.InsertObject(sweep)
      
          def set_Xt(self, xt: str):
              """
              устанавливаем поле Xt для формулы
              :param xt:
              :return:
              """
              self.wave_obj[c4d.PRIM_FORMULA_X] = xt
      
          def set_Yt(self, yt: str):
              """
              устанавливаем поле Yt для формулы
              :param yt:
              :return:
              """
              self.wave_obj[c4d.PRIM_FORMULA_Y] = yt
      
          def set_start_point(self, value: float):
              """
              Устанавливает точку начала волны
              :param value:
              :return:
              """
              self.wave_obj[c4d.PRIM_FORMULA_TMIN] = value
      
          def set_end_point(self, value: float):
              """
              Устанавливает точку окончания волны
              :param value:
              :return:
              """
              self.wave_obj[c4d.PRIM_FORMULA_TMAX] = value
      
          def get_name(self):
              """
              Возвращает имя объекта волны.
              :return:
              """
              return self.wave_obj.GetName()
      
          def get_Yt(self):
              """
              Возвращает формулу из поля Yt.
              :return:
              """
              return self.wave_obj[c4d.PRIM_FORMULA_Y]
      
      
      if __name__ == '__main__':
          WaveMove()
      
          c4d.EventAdd()
      
      
      from typing import Optional
      import c4d
      import math
      import sys
      
      
      def main() -> None:
          doc: c4d.documents.BaseDocument = op.GetDocument()
      
          wave_obj: c4d.BaseObject =
          # рассчитываем фактор скорости
          velocity_factor = doc.GetTime().Get() * wave_obj[c4d.ID_USERDATA, 3] * 0.1
      
          # устанавливаем связи между пользовательскими данными и состоянием волны
          wave_obj[c4d.PRIM_FORMULA_Y] = f"{str(wave_obj[c4d.ID_USERDATA, 2])}*Sin((t+{str(math.radians(wave_obj[c4d.ID_USERDATA, 4]))})*{str(wave_obj[c4d.ID_USERDATA, 1] * 0.01)})"
          wave_obj[c4d.PRIM_FORMULA_TMIN] = velocity_factor
          wave_obj[c4d.PRIM_FORMULA_TMAX] = velocity_factor + 10
          # Update the scene
          c4d.EventAdd()
      
      posted in Cinema 4D SDK 2023 windows python
      L
      ll2pakll
    • RE: How to increase the number of calculations in a Python tag?

      @ferdinand Thank you for your answer, I didn’t think that the solution would be so difficult. From this point of view, unfortunately, the use of the python tag, in a sense, loses its relevance in the case of complex calculations. I tried the option with ExecutePasses, but it completely hangs the entire program. As a result, the only thing that more or less works, without completely reworking the code, is increasing the FPS of the scene from 25 to 100, this is exactly what I wanted - increasing the number of calculations per second. Unfortunately, this does a lot of unnecessary work related to calculating graphics for frames, which I will reduce to 25 per second anyway. Perhaps my comment will help someone who encounters a similar problem, and who, like me, is not a professional programmer. As always, I thank you and wish you a good mood.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • RE: Time Shift. Python tag.

      @ferdinand Thank you very much for your reply. I only need to animate one parameter and I have already written something similar to this parameter cache myself, you saw it in a previous question and advised me to change the location of the cache for more reliability. I will take your advice in the future to avoid reinventing the wheel. Thanks again for your help.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • How to increase the number of calculations in a Python tag?

      Windows 10
      Cinema 4d 2023.2.1

      Good afternoon. I've encountered a problem where the calculation of object behavior written in the Python tag is either not synchronized with the scene frame rate, or otherwise does not have time to calculate some parameters. Because of this, when the amplitude of movements is large, the springs that connect the cubes in the scene look in the wrong direction. In the parameters of the python tag, I have not found a setting for the calculation frequency. Is there any way to increase the number of calculations per frame?
      P.S. I made a video in viewport, but when rendering the picture does not change.

      posted in Cinema 4D SDK windows python 2023
      L
      ll2pakll
    • RE: Python tag doesn't work when rendering

      @ferdinand Your solution of replacing the doc = c4d.documents.GetActiveDocument() line with doc: c4d.documents.BaseDocument = op.GetDocument() really worked. From my point of view, as an ordinary person, your level of understanding of the code seems to be something beyond the realm of possibility. I wish you a good day and thank you again for your help.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • RE: Python tag doesn't work when rendering

      @mogh I just tried it and it didn't make any difference. I don't have many tags in my scene, besides the Python tag there are few targets. I set Python to priority 1, the others to 10, everything stayed the same.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • RE: Python tag doesn't work when rendering

      Apparently I've found a solution, but it doesn't look very convenient. In order for the animation to work in the renderer, you need to bake all objects that are affected by the python code. If there is a more elegant solution to the problem, I will be very grateful to someone who will share it.

      posted in Cinema 4D SDK
      L
      ll2pakll
    • Python tag doesn't work when rendering

      Windows 10
      Cinema 2023.2.1

      Good evening. I am faced with a strange situation. Almost all the behavior in my scene is written using Python tag. In the viewport everything works exactly as it should, but when I try to render the scene only the manually animated object moves, and everything that adjusts the Python tag doesn't work. What could be the problem?
      To understand more about the problem, I made a short video

      .
      Here is the code written in the tag (I apologize for the part of the description written in Russian. I didn't think that this code would be intended for anyone but me.):

      from typing import Optional
      import c4d
      import math
      
      # определяем глобальную переменную для истории
      history = []
      
      def rad_to_deg(rad):
          """
          переводит радианы в градусы
          :param rad:
          :return:
          """
          # Проверяем, является ли rad числом
          if isinstance(rad, (int, float)):
              # Вычисляем градусы по формуле deg = rad * 180 / pi
              deg = rad * 180 / math.pi
              # Возвращаем результат
              return deg
          else:
              # Выводим сообщение об ошибке, если rad не число
              print("Неверный тип аргумента. Ожидается число.")
      
      
      def deg_to_rad(deg):
          """
          Переводит градусы в радианы
          :param deg:
          :return:
          """
          # Проверяем, является ли deg числом
          if isinstance(deg, (int, float)):
              # Вычисляем радианы по формуле rad = deg * pi / 180
              rad = deg * math.pi / 180
              # Возвращаем результат
              return rad
          else:
              # Выводим сообщение об ошибке, если deg не число
              print("Неверный тип аргумента. Ожидается число.")
      
      
      def spring_length_calculation(spring_target: c4d.BaseObject, helix: c4d.BaseObject) -> float:
          """
          Вычисляет длину пружины. В качестве аргументов получает helix и цель для пружины.
          :param spring_target:
          :param helix:
          :return:
          """
          katet_1 = spring_target[c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Y] - helix[
              c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Y]
          katet_2 = spring_target[c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Z] - helix[
              c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Z]
          kvadrat_giopotenuzy = katet_1 ** 2 + katet_2 ** 2
      
          return kvadrat_giopotenuzy ** 0.5
      
      
      def spring_direction_calculation(spring_direction: c4d.BaseObject) -> float:
          """
          Рассчитывает угол направления пружины. Из за того что тег цель работает не корректно, пружина отражается каждый раз
          при пересечении рубежа в 90 градусов, из за этого приходится делать рассчёты её положения таким образом что бы не
          учитывался разворот.
          :param spring_direction: принимает объект
          :return:
          """
          # получаем значение направления направляющей в радианах
          rad_spring_direction = spring_direction[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_Y]
          # получаем значение вращения пружины в радианах
          rad_spring_rotation = spring_direction[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_X]
      
          # переводим радианы в градусы
          deg_spring_rotation = rad_to_deg(rad_spring_rotation)
      
          # смотрим если градус разворота направляющей меньше 10 то направления пружины и направляющей должны быть одинаковыми
          # в ином случае рассчитываем новое направление. Перед тем как вернуть значение переводим его обратно в радианы
          if deg_spring_rotation < 10:
              return rad_spring_direction
          else:
              deg_spring_direction = rad_to_deg(rad_spring_direction)
              d = 180 - deg_spring_direction
              return deg_to_rad(d)
      
      def main() -> None:
          count_oscillators = 15
          doc = c4d.documents.GetActiveDocument()
          for i in range(count_oscillators - 1):
              spring_target = doc.SearchObject(f"spring target {i}")
              helix = doc.SearchObject(f"helix {i}")
              spring = doc.SearchObject(f"spring {i}")
              spring_direction = doc.SearchObject(f"spring direction {i}")
              # изменяем длину пружины в соответсвии с положением цели
              helix[c4d.PRIM_HELIX_HEIGHT] = spring_length_calculation(spring_target, helix)
              # изменяем направление пружины в соответсвии с направляющей для пружины
      
              spring[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_Y] = spring_direction_calculation(spring_direction)
      
          # создаём механику памяти
          # Get the document and the fps
          fps = doc.GetFps()
      
          # Это крайний куб вслед за которым будут двигаться все остальные кубы
          cube_main = doc.SearchObject(f"cube 0")
          pos_main = cube_main[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z]
      
          # Declare the global variable history
          global history
      
          # Create a global variable to store the history of the first cube's position
          # If it doesn't exist, initialize it with the current position
          if not "history" in globals():
              history = [pos_main]
          else:
              # Append the current position to the history list
              history.append(pos_main)
      
          # Get the current frame number
          frame = doc.GetTime().GetFrame(fps)
          # Check if the current frame is the first one
          if frame == 0:
              # Clear the history list
              history = []
              for i in range(1, count_oscillators):
                  # Get the cube by name
                  cube = doc.SearchObject(f"cube {i}")
                  # Set the initial position of the cube
                  cube[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] = 0
      
          # Повторяем движение для остальных кубов с задержкой
          for i in range(1, count_oscillators):
              # Get the cube by name
              cube = doc.SearchObject(f"cube {i}")
              # Calculate the time offset in seconds
              offset = i * 10
              # Calculate the index in the history list
              index = frame - offset
              # If the index is valid, get the position from the history list
              if index >= 0 and index < len(history):
                  pos = history[index]
              # Otherwise, use the initial position
              else:
                  pos = cube[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z]
              # Set the position of the cube
              cube[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] = pos
      
          # Update the scene
          c4d.EventAdd()
      
      posted in Cinema 4D SDK windows 2023 python
      L
      ll2pakll