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
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. s_bach
    3. Best
    S
    • Profile
    • Following 0
    • Followers 0
    • Topics 2
    • Posts 257
    • Best 128
    • Controversial 0
    • Groups 0

    Best posts made by s_bach

    • RE: Want to show text in dialog box similar to "Help" menu of Cinema 4D(only text)

      Hello,

      the "Help" does not use a GeUserArea.

      The "Help" is displayed using the HtmlViewerCustomGui GUI element. This GUI element encapsulates the local browser to display a HTML document.

      If you want to create the same user experience, you can simply use that GUI element to load a custom HTML document that presents your information.

      • HtmlViewerCustomGui (C++)
      • HtmlViewerCustomGui (Python)
      • Custom GUI Elements

      As always you can use the Q&A Functionality to mark a thread as a question.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: NewMem question

      Hello,

      Cinema 40 R20 introduces a new, elaborate error handling system. A function can return now a dedicated result object that stores additional information if the operation was successful or not.

      NewMem does return such a result value. You find information on how to handle errors in the documentation: Error Handling. See also Error System in the API Transition.

      That being said, in most cases there should be no reason to allocate memory with NewMem. It is recommended to use arrays like BaseArray instead. Such an array provides save access to and management of the allocated data.

      See

      • Memory Allocation
      • BaseArray Manual

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Miscellaneous questions about "BaseContainer","DescID" etc

      Hello,

      An object like BaseObject can store data. This data is typically stored in the object's BaseContainer. But there are cases where the data is not stored in the BaseContainer so in general it is saver to use GetParameter().

      A BaseContainer can store any kind of data. This includes other BaseContainers. The BaseContainer of the object stores a sub-BaseContainer that stores all the values of the user data. The ID of the User-Data-BaseContainer is c4d.ID_USERDATA. Using this ID you can obtain the BaseContainer:

      bc = op.GetData()
      userDataValues = bc[c4d.ID_USERDATA]
      
      # or
      
      userDataValues = op.GetParameter(c4d.ID_USERDATA, c4d.DESCFLAGS_GET_0)
      

      This BaseContainer stores the parameter values of user data parameters.

      A parameter is identified using a DescID. This DescID has nothing to do with UUID.

      A DescID is composed of one or several DescLevel components. In most cases the DescID has only one DescLevel. The DescLevel defines these properties:

      • id: id of the level. Typically the parameter ID.
      • dtype: type of the parameter.
      • creator: ID of the "creator" of the parameter. E.g. on a cube you have some parameters that were created by the "Cube" (Size, Segments, ...). But there are also other parameters that are shared by all BaseObject's (Position, Scale, ...). These shared parameters are inherited from a base class, in this case Obase (5155). The "creator" value is not always relevant so it might not always be set.

      A DescID can have multiple DescLevels since a parameter may be stored inside another parameter. As described above, user data values are stored in a BaseContainer that is stored in the default BaseContainer. The full DescID of a user-data parameter has to reflect that. The DescID of a user-data parameter must first identify the user-data container and then the actual parameter in that container. This is done using two DescLevels:

      id = c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, 0), c4d.DescLevel(1))
      
      value = op.GetParameter(id, c4d.DESCFLAGS_GET_0)
      

      But the Python API also allows you to do the same with less code:

      value = op[c4d.ID_USERDATA, 1]
      

      Or you access the user-data BaseContainer:

      bc = op.GetData()
      userDataValues = bc[c4d.ID_USERDATA]
      value = userData[1]
      

      The DescIDs are not necessarily stored in a file. The number of parameters of an object may change depending on how the object is used. The list of all displayed parameters is stored in the parameter Description. This Description contains the IDs as well as information on how to display the parameters in the Attribute Manager. The information how the parameter should be displayed is also stored as a BaseContainer (but this is NOT the object's BaseContainer).

      The parameter description of user data parameters is stored in the object. You can access this data with GetUserDataContainer():

      for id, descriptionBC in op.GetUserDataContainer():
      	print("User Data ID: " + str(id))
      	print("User Data Value: " + str(op[id]))
      	print("User Data Name: " + descriptionBC[c4d.DESC_NAME])
      

      To make that clear: the VALUES of user data parameters are stored in the object's BaseContainer. The DESCRIPTION of the user data parameters is stored in the BaseContainers accessed with GetUserDataContainer().

      You find an overview over the most fundamental concepts and classes currently in the C++ documentation under Foundations, especially Classic API Base Classes.

      You can also find further information about these classes:

      • BaseContainer, BaseContainer Manual
      • DescID, DescID Manual
      • DynamicDescription Manual
      • Description Manual
      • Description Settings Manual

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Dynamically changing icons

      Hello,

      you can dynamically change the icon of ObjectData and TagData based plugins by implementing NodeData.Message() and reacting to the message MSG_GETCUSTOMICON. There you can define the BaseBitmap used as the icon. Something like this:

      if type == c4d.MSG_GETCUSTOMICON:
      
          # load a standard Cinema icon
          icon = c4d.bitmaps.InitResourceBitmap(c4d.RESOURCEIMAGE_OK)
          data["bmp"] = icon
          data["w"] = icon.GetBw()
          data["h"] = icon.GetBh()
          # set as filled
          data["filled"] = True
      
          return True
      

      You find related information in the C++ documentation (NodeData::Message() Manual).

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Ovolumeloader

      Hello,

      a volume loader is just an ordinary generator. This means the stuff it creates is in its cache. You can access the volumes stored in its cache as usual:

      if (baseObject->IsInstanceOf(Ovolumeloader) == false)
        return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION);
      
      BaseObject* const cache = baseObject->GetCache();
      if (cache == nullptr)
        return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION);
      
      if (cache->IsInstanceOf(Ovolume) == false)
        return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);
      
      const VolumeObject* const volumeObject = static_cast<VolumeObject*>(cache);
      
      ApplicationOutput("Volume Object: " + volumeObject->GetName());
      

      See also VolumeObject Manual.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Universal "Undo" to Rule them All?

      Hello,

      as Cairyn pointed out, the undo system does not support nesting.

      So calling CallCommand() multiple times is the same as pressing multiple buttons in the GUI - it will create multiple undo steps. The only alternative is to not use CallCommand() but to do the specific operations (using the API) yourself.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Modular plugin programming

      Hello,

      you might have to add the pyp file path to the system path using:

      sys.path.append(os.path.dirname(__file__))
      

      so

      import sys
      import os
      sys.path.append(os.path.dirname(__file__))
      
      
      import hello_function
      

      best wishes,
      Sebastian

      posted in General Talk
      S
      s_bach
    • Goodbye

      Hello,

      at the end of this week, I will leave Maxon for a new direction in my life.

      In the last five years, I tried to give you the best answers to your questions and to provide you with documentation and examples that would make your daily life easier.

      During my time here, I met remarkable developers and learned about their projects. I learned a lot by working with you here on the forum, via mail or in person. Especially, I want to thank Riccardo, Maxime and Manuel for being great colleagues and an amazing team. Be assured that this team will continue to work with great commitment to make sure you get the support you need.

      I wish you all the best.
      Sebastian

      posted in News & Information
      S
      s_bach
    • RE: styleguide warnings

      Hello,

      you can disable style checks in the projectdefinition.txt file of your project. See Stylecheck.

      Also just a reminder that only Visual Studio 2015 is officially supported to develop Cinema 4D R20 plugins.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: NewMem question

      Hello,

      the best solution is probably to create the BaseArray in the caller and hand over a reference to that BaseArray. Your function can then safely fill the BaseArray:

      maxon::Result<void> GetVertexNormals(PolygonObject * const pObj, maxon::BaseArray<Vector>& normals)
      

      If you encounter unexpected break points, please open a new thread to discuss this issue.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: getting 3D noise values

      Hello,

      as @eZioPan has shown you can use the C4DNoise class to sample a noise. Additionally, you find further noises in the c4d.utils.noise namespace.

      A noise function typically just returns a scalar value. So if you want to create a color you have to combine different noises (with different seeds) or you sample the same noise with different offsets.

      Please also make sure to mark your post as a question using our Q&A system.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Get type name by Id

      And if you are only interested in BaseObjects, there is also GetObjectName().

      See BaseObject Manual.

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: FILENAME attribute extension filter

      Hello,

      a FILENAME parameter has no options to set a suffix.

      If you want to only select files of a specific type you could implement SetDParameter() to check if the given Filename is acceptable.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Get clone of group and keep links

      Hello and welcome to the PluginCafe,

      Please post your questions regarding the Cinema 4D API in the "Cinema 4D Development" forum.

      Also, please use tags and the Q&A system to mark questions. Please read "Read Before Posting".

      To handle links when coping objects one can use a AliasTrans object. This object allows to fix links after objects are copied.

      trans = c4d.AliasTrans()
      if trans is None or trans.Init(doc) is False:
          return False
      
      objects = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_0)
       
      for obj in objects:
           
          clone = obj.GetClone(c4d.COPYFLAGS_0, trans)
              
          if clone is not None:
              clone.SetName(obj.GetName() + " clone")
              doc.InsertObject(clone, None, None)
               
      c4d.EventAdd()
      

      You find more information in the C4DAtom Manual or the AliasTrans Python documentation.

      Best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: ColorDialog Flags

      Hello,

      ColorDialog is just the Python version of C++'s GeChooseColor() function.

      It uses the flags DR_COLORFIELD_ICC_BASEDOC and DR_COLORFIELD_ICC_BPTEX which just define the proper color profile. Typically is is enough to just set 0.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Acessing BaseShader properties

      Hello,

      there should be header files for all shaders.

      The header file of Xgradient is xslagradient.h.

      The header file of Xvertexmap is xslavertexmap.h.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: iCustomGui with multiple input fields and TriState

      Hello,

      actually, three-component vectors (DTYPE_VECTOR) and four-component vectors (DTYPE_VECTOR4D) behave differently. DTYPE_VECTOR shows <<Multiple>> component-based while DTYPE_VECTOR4D shows <<Multiple>> for all components if only one component is different.

      DTYPE_VECTOR and CUSTOMGUI_VECTOR are very old parts of Cinema 4D, so they are not implemented as iCustomGui plugins. DTYPE_VECTOR4D displays <<Multiple>> for all components if one component is different. So I'm afraid there isn't any source code within Cinema that does what you want to do.

      The typical procedure is to obtain the '"tristate" using

      _isTristate = tristate.GetTri();
      

      and then using this value with the GeDialog functions. E.g. the last argument of GeDialog::SetFloat() is a "tristate" argument.

      If your custom data type should behave like DTYPE_VECTOR4D, then it must display <<Multiple>> for all components if only one component is different.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Python Scripting-Help with GeDialog.AddComboBox add child option

      Hello and welcome to the PluginCafe,

      please post your questions regarding the Cinema 4D APIs in the "Cinema 4D Development" category. Also, please use tags and the Q&A system. You find all that information in "Read Before Posting".

      Is your question how to get the currently selected item of the combo box?

      You can implement the "Command" function of your dialog to be informed when the selected value changed. This way you could rename the object when that happens.

      Alternatively, you can access the selected element using a custom member function of the dialog. You can use the returned value to do what you want in the caller code.

      Something like this:

      class TestDialog(gui.GeDialog):
          
          def CreateLayout(self):
      
              self.AddComboBox(5000, c4d.BFH_LEFT, 100, 10, False)
              self.AddChild(5000, 0, "Child 0")
              self.AddChild(5000, 1, "Child 1")
              self.AddChild(5000, 2, "Child 2")
      
              return True
      
          def Command(self, id, msg):
      
              # print currently selected "child""
              if id == 5000:
                  value = self.GetInt32(5000)
                  print("got value: " + str(value))
      
      
              return c4d.gui.GeDialog.Command(self, id, msg) 
          
          # custom function
          def GetSelectedChild(self):
              return self.GetInt32(5000)
      
      def main():
          dialog = TestDialog()
          
          dialog.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE, 132456, -1 ,-1 ,400 ,400)
          
          value = dialog.GetSelectedChild()
          
          op.SetName(str(value))
          c4d.EventAdd()
      

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Retrieve the newly created object from the "IK_TAG_ADD_GOAL" command?

      Hello,

      using CallButton() is the same as pressing the button in the GUI. So the CallButton() function does not return anything.

      The "Add Goal" functionality creates a new Null object. This null object is inserted after the host object of the tag. Sou you could simply get the "next" object to get the goal object.

      tag = doc.GetActiveTag()
      if not tag:
          return
          
      # should check for type of course
      # ...
      
      c4d.CallButton(tag, c4d.ID_CA_IK_TAG_ADD_GOAL)
      
      # assume that the newly created null object is now the next object after the host object
      hostObject = tag.GetObject()
      nextObject = hostObject.GetNext()
      
      print(nextObject)
      

      Alternatively, you could simply read the now defined BaseLink to the goal object:

      c4d.CallButton(tag, c4d.ID_CA_IK_TAG_ADD_GOAL)
      goalObject = tag[c4d.ID_CA_IK_TAG_TARGET]
      

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach
    • RE: Universal "Undo" to Rule them All?

      Hello,

      using CallCommand() is the same as pressing the button in the GUI. This means that if you use CallCommand(), the complete command is executed - including any undo-creation within that command.

      You find an overview over the undo system in the C++ documentation: Undo System Manual.

      best wishes,
      Sebastian

      posted in Cinema 4D SDK
      S
      s_bach