Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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
    • Recent
    • Tags
    • Users
    • Register
    • Login
    1. Maxon Developers Forum
    2. ThomasB
    Offline
    • Profile
    • Following 1
    • Followers 0
    • Topics 33
    • Posts 115
    • Groups 0

    ThomasB

    @ThomasB

    4
    Reputation
    45
    Profile views
    115
    Posts
    0
    Followers
    1
    Following
    Joined
    Last Online
    Age 49
    Location Germany

    ThomasB Unfollow Follow

    Best posts made by ThomasB

    • RE: How to create python plugin in 2024?

      Yes, download the SDK and study the examples.
      I wrote my own software that allows me to simply select the type of plugin I want to create, enter the plugin ID and the plugin name. The program then creates the folder structure, all files with the correct content and the correct name automatically...that was the first thing I did because it is always extremely tedious.
      This then takes 10 seconds and I have a finished blueprint. Then I can start programming straight away.
      I usually make the Gui first using UserData to roughly create the design and then I write it down in the resfile in no time at all. It's relatively quick and even fun and you gradually grow into it, it emerges little by little.....

      Basically it wouldn't be a problem to write a script that writes the UserData interface into a Res, header and string file, I already have an idea for that... That would actually be easy to do.
      I'll get to it when I'm done with my update... and then maybe make it available to the community... However, a similar one is already there, but I can't remember where.

      Greetings
      Tom

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • RE: Boole Object causes version 2024 to freeze

      @ferdinand
      Hello Ferdinand,
      Thank you very much first of all.
      yes, you're right, I worked extremely sloppily with the SMC method, of course I'll take the threading into account and also work with a Temp Document.

      Regarding the problem itself, I can only say that reinstalling CINEMA 4D solved our problem.

      Cheers

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • RE: How do I get the cache of a cloner in an ObjectData Plugin

      @ferdinand
      oh man thanks I forgot this

      BaseDocument.ExecutePasses(bt=None, animation=False, expressions=False, caches=True, flags=c4d.BUILDFLAGS_NONE)
      

      I tried that before but it seems I have set the caches parameter wrong.
      Thank you very much....

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • RE: Best way to hide a child and get best perfomance

      @Tpaxep
      besides that I have a code which works for your scenario.

      def GetVirtualObjects(self, op, hh):
      
          profile_orig = op.GetDown()
          if profile_orig is None:
              return None
      
          path_orig = profile_orig.GetNext()
          if path_orig is None:
              return None
      
          # you need to make clones of the children, don't use the orig
          # first make clones and then use the GACHC Methode below
          profile = profile_orig.GetClone()
          path = path_orig.GetClone()
      
          dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_ALL)
      
          child_dirty = op.GetAndCheckHierarchyClone(hh, profile_orig, c4d.HIERARCHYCLONEFLAGS_ASSPLINE, True)         
      
          if not any([dirty, child_dirty["dirty"]]):
              return child_dirty["clone"]
      
          print("GVO Executed")
          # calculation of your geometry
          sweep = c4d.BaseObject(c4d.Osweep)
          tag = c4d.BaseTag(c4d.Tphong)
          tag[c4d.PHONGTAG_PHONG_USEEDGES] = False
          sweep.InsertTag(tag)
          path.InsertUnder(sweep)
          profile.InsertUnder(sweep)
      
          return sweep
      

      you can also track dirty manually:

      def GetVirtualObjects(self, op, hh):
      
              profile_orig = op.GetDown()
              if profile_orig is None:
                  return None
      
              path_orig = profile_orig.GetNext()
              if path_orig is None:
                  return None
      
              profile = profile_orig.GetClone()
              path = path_orig.GetClone()
      
              dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_ALL)
      
              #Manually track dirty of the childs (Example)      
              child_dirty = False
              for child in op.GetChildren():
                  child_dirty = child.IsDirty(c4d.DIRTYFLAGS_DATA | c4d.DIRTYFLAGS_MATRIX)
                  if child_dirty:
                      break
      
              # After Dirty Touch the childs
              profile_orig.Touch()
              path_orig.Touch()
      
              if not any([dirty, child_dirty]):
                  return op.GetCache(hh)    
      
              print("GVO Executed")
              # calculation of your geometry
              sweep = c4d.BaseObject(c4d.Osweep)
              tag = c4d.BaseTag(c4d.Tphong)
              tag[c4d.PHONGTAG_PHONG_USEEDGES] = False
              sweep.InsertTag(tag)
              path.InsertUnder(sweep)
              profile.InsertUnder(sweep)
      
              return sweep
      
      posted in Cinema 4D SDK
      ThomasBT
      ThomasB

    Latest posts made by ThomasB

    • RE: Thread safety when handling CTrack in TagData.Message() on button click

      @ferdinand
      WTF is wrong with me? It should work now.

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • RE: Thread safety when handling CTrack in TagData.Message() on button click

      @ferdinand

      Oh sorry, I missed that.
      Is Google Drive ok?

      Test Plugin Download

      (which I assume is what you mean with 'running (an) animation').

      By "running during animation," I meant clicking the button while the animation is playing.

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • Thread safety when handling CTrack in TagData.Message() on button click

      Hi,
      I have a small plugin example of a Tag Plugin and I want to know if I handle the threading correctly, because in my production version , I encountered some freezes, espacially during a running animation or when Redhift Render View was running.

      Download Plugin:
      test_tag_plugin.zip

      Quick explanation:

      • tag plugin with a button and a link field
      • button click has to load a sound
      • When the button is clicked, I want to alternate between loading a sound into the track specified in the link field (In the official release, I calculate a sound, save it to the hard drive, and load it into the track.)
      • If no track exists, one must be created.
      • I handle this within the Message Method as usual
      • and check using c4d.threading.GeIsMainThread().

      Is this the right approach, or are there other factors to consider?
      Should I perform the check inside the add_sound method instead,
      or would it be better to check using c4d.GeIsMainThreadAndNoDrawThread?

      Please bear in mind that I threw this together quickly; you shouldn't expect perfection in the code—such as error handling—as my only concern right now is thread safety.

      Code:

      """
      Simple CTrack Sound Loader
      """
      
      import c4d
      import os
      from c4d import bitmaps, plugins
      
      # be sure to use a unique ID obtained from www.plugincafe.com
      PLUGIN_ID = 123456789
      
      PY_ADD_AUDIO = 1000
      PY_TRACK = 1001
      
      class TestTagPlugin(plugins.TagData):
          def __init__(self):
      
              self.current_sound: str | None = None
      
          def Init(self, node, isCloneInit):
      
              if not isCloneInit:
                  self.InitAttr(node, c4d.BaseList2D, c4d.DescID(PY_TRACK))
      
      
                  node[PY_TRACK] = None
              return True
      
          def Execute(self, tag, doc, op, bt, priority, flags):
              return c4d.EXECUTIONRESULT_OK
      
          @staticmethod
          def check_track(node, sound_path):
              """
              Checks if there is a track in the audiotrack slot, if not it creates one and puts the sound into this track
              Has to be called from the MainThread
              :param node: the tag instance
              :param sound_path: str - The path to the sound file
              """
              track = node[PY_TRACK]
      
              if not track or not track.GetType() == c4d.CTsound:
      
                  # create special Audio Track
                  obj: c4d.BaseObject = node.GetObject()
      
                  track = c4d.CTrack(obj, c4d.DescID(c4d.DescLevel(c4d.CTsound, c4d.CTsound, 0)))
      
                  obj.InsertTrackSorted(track)
      
                  node[PY_TRACK] = track
                  track.SetName("Audio_Track")
      
              node[PY_TRACK][c4d.CID_SOUND_NAME] = ""
      
              node[PY_TRACK][c4d.CID_SOUND_NAME] = sound_path
      
      
          def msg_add_audio(self, node) -> bool:
              """
              Loads the audio into the track
              :param node: the tag instance
              :return: bool
              """
              was_animation_running = False
              if c4d.CheckIsRunning(c4d.CHECKISRUNNING_ANIMATIONRUNNING):
                  # save animation state and stop animation
      
                  was_animation_running = True
                  c4d.CallCommand(12412)
      
              if self.current_sound == "base_1.wav":
                  self.current_sound = "base_2.wav"
              else:
                  self.current_sound = "base_1.wav"
      
              path = os.path.join(sample_path, self.current_sound)
      
              if not os.path.exists(path):
                  return False
      
              self.check_track(node, path)
      
              if was_animation_running:
                  # restart stopped Animation
      
                  c4d.CallCommand(12412)
      
              return True
      
          def Message(self, node: c4d.GeListNode, type: int, data: object) -> bool:
      
              if type == c4d.MSG_DESCRIPTION_COMMAND:
      
                  if data["id"][0].id == PY_ADD_AUDIO:
                      if c4d.threading.GeIsMainThread():
                          self.msg_add_audio(node)
      
                      return True
      
              return True
      
      # main
      if __name__ == "__main__":
      
          bmp = bitmaps.BaseBitmap()
          p_dir, file = os.path.split(__file__)
      
          fn = os.path.join(p_dir, "res", "icon.tif")
          bmp.InitWith(fn)
      
          sample_path = os.path.join(p_dir, "res", "samples")
      
          c4d.plugins.RegisterTagPlugin(id=PLUGIN_ID,
                                        str="test_tag_plugin",
                                        info=c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE,
                                        description="test_tag_plugin",
                                        g=TestTagPlugin, icon=bmp)
      
      

      An additional question regarding the tag:
      I’m also curious about a scenario where I launch a GeDialog from the tag via a button and pass the tag to it and toggle the sound from within the dialog as well. Would using c4d.SpecialEventAdd() keep me on the safe side—for instance. So if the dialog runs asynchronously, stores the tag as a member variable,
      retrieves the class instance via GetNodeData, and then calls the add_sound function?
      Is that a viable approach? It works in the official plugin.

      Thanks in advance.
      T.B.

      posted in Cinema 4D SDK 2026 python windows
      ThomasBT
      ThomasB
    • RE: Best way to hide a child and get best perfomance

      @Tpaxep
      besides that I have a code which works for your scenario.

      def GetVirtualObjects(self, op, hh):
      
          profile_orig = op.GetDown()
          if profile_orig is None:
              return None
      
          path_orig = profile_orig.GetNext()
          if path_orig is None:
              return None
      
          # you need to make clones of the children, don't use the orig
          # first make clones and then use the GACHC Methode below
          profile = profile_orig.GetClone()
          path = path_orig.GetClone()
      
          dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_ALL)
      
          child_dirty = op.GetAndCheckHierarchyClone(hh, profile_orig, c4d.HIERARCHYCLONEFLAGS_ASSPLINE, True)         
      
          if not any([dirty, child_dirty["dirty"]]):
              return child_dirty["clone"]
      
          print("GVO Executed")
          # calculation of your geometry
          sweep = c4d.BaseObject(c4d.Osweep)
          tag = c4d.BaseTag(c4d.Tphong)
          tag[c4d.PHONGTAG_PHONG_USEEDGES] = False
          sweep.InsertTag(tag)
          path.InsertUnder(sweep)
          profile.InsertUnder(sweep)
      
          return sweep
      

      you can also track dirty manually:

      def GetVirtualObjects(self, op, hh):
      
              profile_orig = op.GetDown()
              if profile_orig is None:
                  return None
      
              path_orig = profile_orig.GetNext()
              if path_orig is None:
                  return None
      
              profile = profile_orig.GetClone()
              path = path_orig.GetClone()
      
              dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_ALL)
      
              #Manually track dirty of the childs (Example)      
              child_dirty = False
              for child in op.GetChildren():
                  child_dirty = child.IsDirty(c4d.DIRTYFLAGS_DATA | c4d.DIRTYFLAGS_MATRIX)
                  if child_dirty:
                      break
      
              # After Dirty Touch the childs
              profile_orig.Touch()
              path_orig.Touch()
      
              if not any([dirty, child_dirty]):
                  return op.GetCache(hh)    
      
              print("GVO Executed")
              # calculation of your geometry
              sweep = c4d.BaseObject(c4d.Osweep)
              tag = c4d.BaseTag(c4d.Tphong)
              tag[c4d.PHONGTAG_PHONG_USEEDGES] = False
              sweep.InsertTag(tag)
              path.InsertUnder(sweep)
              profile.InsertUnder(sweep)
      
              return sweep
      
      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • RE: Educational Licenses

      @ferdinand

      So when I catch it in the Message method ,with c4d.C4DPL_STARTACTIVITY
      how do I deactivate the plugin in case the license check went wrong?

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • RE: Maxon API for Python

      @ferdinand
      Thank you for your detailed explanation, it shed some light on the whole matter for me.

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • Maxon API for Python

      Apologies in advance if this question is out of scope.
      Is the Maxon API already fully usable for Python, or can it completely replace the classic API? I find it very difficult to understand right now. Or is it more intended for initializing data types and using it in conjunction with the classic API?
      And if not, when will it finally be fully usable for Python?

      posted in Cinema 4D SDK python 2025 windows
      ThomasBT
      ThomasB
    • RE: TempUVHandle always None! Why?

      @i_mazlov
      Thank you very much for the hint with the texture view and for the helpful links.
      The second thread I have already read, but was not able to find the solution so far. I study these examples. Thank you.

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB
    • TempUVHandle always None! Why?

      Hi,
      I am not able to get the TempUVHandle from an active UVWTag in R21.
      It returns always "None".
      In the following script I created a simple plane in a TempDoc and took the cache and inserted it into the TempDoc. It already has a UVW-Tag, then I have set the object active and so the UVWTag....

      My Code:

      import c4d
      
      def main():
          temp = c4d.documents.BaseDocument()
      
          plane = c4d.BaseObject(c4d.Oplane)
          temp.InsertObject(plane)
          temp.ExecutePasses(bt=None, animation=False, expressions=False, caches=True, flags=c4d.BUILDFLAGS_NONE)
          
          plane_cache = plane.GetCache().GetClone()
      
          if plane_cache is None:
              raise Exception("no cache")
      
          temp.InsertObject(plane_cache)
          uvw_tag = plane_cache.GetTag(c4d.Tuvw)   
          temp.SetActiveTag(uvw_tag)
          temp.SetActiveObject(plane_cache)
      
          handle = c4d.modules.bodypaint.GetActiveUVSet(temp, c4d.GETACTIVEUVSET_UVWS)
          
          print(handle)
          
      
      # Execute main()
      if __name__=='__main__':
          main()
      
      posted in Cinema 4D SDK r21 python windows
      ThomasBT
      ThomasB
    • RE: Dynamically adding parameters in tag plugin - description is not refreshing

      @ferdinand
      Thanks a lot Ferdinand for your time and effort. It is always admirable how carefully and thoroughly you answer many questions.
      At first it was often difficult to understand and follow your code examples...now it is a little easier.
      Thanks for that.

      I found out that the following method also does the job:

      c4d.SendCoreMessage(c4d.COREMSG_CINEMA, c4d.BaseContainer(c4d.COREMSG_CINEMA_FORCE_AM_UPDATE), 0)
      

      Sorry for the second question about why this

      buttonDesc[c4d.DESC_FITH] = True
      buttonDesc[c4d.DESC_SCALEH] = True
      

      in the GetDDescription method are not working. I thought this is a follow-up question.
      I will open another topic for that.

      posted in Cinema 4D SDK
      ThomasBT
      ThomasB