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. m_adam
    M
    • Profile
    • Following 0
    • Followers 8
    • Topics 3
    • Posts 1,451
    • Best 518
    • Controversial 0
    • Groups 2

    m_adam

    @m_adam

    745
    Reputation
    901
    Profile views
    1.5k
    Posts
    8
    Followers
    0
    Following
    Joined Last Online

    m_adam Unfollow Follow
    Global Moderator administrators

    Best posts made by m_adam

    • RE: Sampling the falloff of a mograph effector? (python/R19)

      Hi @jenandesign, first of all, welcome in the plugincafe community.

      No worry at all, since it's your first post, but I would like to point you about few rules in order to properly ask questions in this forum for future ones (I've set up this one).

      • How to Post Questions (Especially the tagging system).
      • Q&A New Functionality.

      With that's said. Regarding your question. I get it to work (a bit hacky) but it's working.
      When C4D render a scene, actually the whole scene is cloned in memory and then it renders this one. (It allow a user to still work on C4D while the other scene is rendering).
      So this render scene is read-only, you shouldn't modify at anytime anything in this scene, or you can screw up the render.
      Moreover in a shaderData, in order to optimize memory, only relevant information (aka polygon, UV) are copied. So you are losing your UserData.
      This means you have to find another way to find your effector.
      So the way I find to achieve what you want is to get the document from the object passed to the shader(the rendered document).
      Then in this document, I search for an effector called "Plain" (Not the most robust way but it's working).

      Then here the final code I put on the python color channel of a sketch and toon

      import c4d
      
      def main():
          doc = op.GetDocument()
          effector = doc.SearchObject("Plain")
          if not effector: return c4d.Vector()
          
          eff =  c4d.modules.mograph.C4D_Falloff()
          if not eff: return c4d.Vector()
          eff.InitFalloff(bc=effector.GetData())
      
          eff.SetMg(effector.GetMg())
          eff.SetMode(effector[c4d.FALLOFF_MODE])
      
           # World position currently sampled
          value = eff.Sample(wPnt, False)
          return c4d.Vector(value)
      
      

      And the attached scene where a plain effector drives a mograph effector and the sketch and toon shader.
      0_1542626486830_SketchAndToon.c4d
      0_1542626902040_sketchtoon.jpg

      If you have any questions, please let me know!
      Cheers,
      Maxime!

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: 3rd party APIs into pyp/pypv file

      Hi @merkvilson first of all pip and Niklas localimport module is completely two different things.

      • pip is a python module manager (similar to package manager in unix). This means once it's installed to a python binary, it will be able to manage other modules, download them, install them with all their dependencies and update them and so on.
      • localimport is a python module that let you manage import in a local way while in Cinema 4D by default is global. For more information please read how the import function works in python.
        Meaning if you add a module named xyz to the sys.path from your plugin A and from plugin B you try to import another module named xyz, it will load the one from plugin A (the first one register).

      Now how to install pip?

      1. Downloads https://bootstrap.pypa.io/get-pip.py.
      2. Downloads the c4dpy[URL-REMOVED] version matching your Cinema 4D version. (Already included in R21)
      3. Moves c4dpy and get-pip.py in the Cinema 4D folder.
      4. Opens a shell to the Cinema 4D folder:
        • Window: Type cmd in the top path and press Enter.
        • Mac: Open a new shell, then with cd navigate to your Cinema 4D folder. (You can drag and drop the path).
      5. Runs this command line c4dpy get-pip.py. This will execute the get-pip script which will, download and install the pip module.
      6. Now you can start to play with pip c4dpy -m pip install numpy. c4dpy allow to runs any module with the -m argument see c4dpy commandline. in this case I asked to pip to download and install numpy but any other 3rd party compatible with pip can be downloaded and used.

      With that's said we do not provide support for 3rd party modules, so if it does not work with Cinema 4D Python environment, we can't help you.

      Cheers,
      Maxime.


      [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Setting `coordinate system` using `MCOMMAND_MIRROR`

      Hi @kisaf,

      MCOMMAND_MIRROR is indeed a bit special because when you click on the Apply button from the GUI, it does send a message to some c4d elements, then these elements read MDATA_MIRROR_VALUE, MDATA_MIRROR_SYSTEM and MDATA_MIRROR_PLANE calculates values for MDATA_MIRROR_POINT and MDATA_MIRROR_VECTOR and then call the SendModelingCommand. Unfortunately, there is no way for you to send this message, and the best way to do it is to recalculate the stuff as bellow.

      import c4d
      
      
      def main():
          # Checks if selected object is valid
          if op is None:
              raise ValueError("op is none, please select one object.")
      
          # Defines some general settings, take care MDATA_MIRROR_VALUE, MDATA_MIRROR_SYSTEM and MDATA_MIRROR_PLANE
          settings = c4d.BaseContainer()
          settings[c4d.MDATA_MIRROR_DUPLICATE] = True
          settings[c4d.MDATA_MIRROR_SELECTIONS] = True
          settings[c4d.MDATA_MIRROR_ONPLANE] = True
          settings[c4d.MDATA_MIRROR_WELD] = True
          settings[c4d.MDATA_MIRROR_TOLERANCE] = 0.01
      
          # Corresponds to MDATA_MIRROR_VALUE, MDATA_MIRROR_SYSTEM and MDATA_MIRROR_PLANE
          value = 1000.0
          system = 0 # 0 = Object, 1 = World
          plane = 1 # 0 = XY, 1 = ZY, 2 = XZ
      
          if not 0 <= system < 2:
              raise ValueError("System can only be 0 or 1")
      
          # Buffer position vector
          pos = c4d.Vector()
      
          # Calculates Local (Object) coordinates
          if system == 0:
              globalMatrix = op.GetMg()
              if plane == 0:
                  pos = globalMatrix.v3
              elif plane == 1:
                  pos = globalMatrix.v1
              elif plane == 2:
                  pos = globalMatrix.v2
      
              settings[c4d.MDATA_MIRROR_POINT] = globalMatrix.off + pos * value
              settings[c4d.MDATA_MIRROR_VECTOR] = pos
      
          # Calculates World coordinates
          elif system == 1:
              if plane == 0:
                  pos = c4d.Vector(0.0, 0.0, 1.0)
              elif plane == 1:
                  pos = c4d.Vector(1.0, 0.0, 0.0)
              elif plane == 2:
                  pos = c4d.Vector(0.0, 1.0, 0.0)
      
              settings[c4d.MDATA_MIRROR_POINT] = pos * value
              settings[c4d.MDATA_MIRROR_VECTOR] = pos
      
          # Send the Modeling Operation
          res = c4d.utils.SendModelingCommand(c4d.MCOMMAND_MIRROR,
                                              [op],
                                              c4d.MODELINGCOMMANDMODE_ALL,
                                              settings,
                                              doc)
      
          c4d.EventAdd()
      
      
      if __name__ == "__main__":
          main()
      
      

      Hope it solves your issue, if you have any question, please let me know.
      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Get World Position of Obj A's position and Set it to Obj B's position

      So regarding your script why it crashes, it's simply because you make an infinite loop with this code since you add each time something more to iterate.

      for idx in newPos:
          newPos.append(GlobalToLocal(oldObject, idx))
      

      Just to make it cleaner I've called objA and objB.
      Then the needed steps are:

      1. Get World Position from points of objB.
      2. Convert these World Position in the local space of objA.

      So as you may be already aware regarding our Matrix Fundamentals where is how to achieve both operations.

      • Local Position to World Position corresponds to the obj global matrix where the position is locals to multiplied by the local position.
      • World Position to Local Position corresponds to the inverse obj global matrix where the position will be locals multiplied by the world position.

      Finally, before providing with the solution I would like to remind you to please always execute your code in the main function. The main reason is that if you transform your script as a button in the Dialog, every code that is in the global scope of your script is executed for each redraw of the Cinema 4D GUI (which is pretty intensive!). Moreover please always check for objects, if they exist or not try to adopt a defensive programming style in order to avoid any issues.

      import c4d
      
      def main():
          # Declares object variables
          ObjA = doc.SearchObject("A")
          ObjB = doc.SearchObject("B")
      
          # Checks if objects are found
          if ObjA is None or ObjB is None:
              raise ValueError("Can't found a and b object")
      
          # Checks if they are polygon object
          if not isinstance(ObjA, c4d.PolygonObject) or not isinstance(ObjB, c4d.PolygonObject):
              raise TypeError("Objects are not PolygonObject")
      
          # Checks Point count is the same for both obj
          allPtsObjA = ObjA.GetAllPoints()
          allPtsObjB = ObjB.GetAllPoints()
          if len(allPtsObjA) != len(allPtsObjB):
              raise ValueError("Object does not get the same pount count")
      
          # Retrieves all points of B in World Position, by multipling each local position of ObjB.GetAllPoints() by ObjB.GetMg()
          allPtsObjBWorld = [pt * ObjB.GetMg() for pt in allPtsObjB]
      
          # Retrieves All points of B from World to Local Position of ObjA, by multipling each world position of ObjB by the inverse objA.GetMg()
          allPtsObjANewLocal = [pt * ~ObjA.GetMg() for pt in allPtsObjBWorld]
          
          # Sets New points position
          ObjA.SetAllPoints(allPtsObjANewLocal)
          ObjA.Message(c4d.MSG_UPDATE)
          
          # Updates Cinema 4D
          c4d.EventAdd()
      
      # Execute main()
      if __name__=='__main__':
          main()
      

      If you have any questions, please let me know.
      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: How to deepcopy DescID?

      Hi @mikeudin, due to how our Classic Python API is built deepcopy does not work since our python objects only store pointer to our internal C++ object and not directly to other Python objects, so deepcopy fail in this regards since, the deepcopy operation, free and try to recreate these data, which is not possible since they are not PyObject but real C++ object but copy.copy does works, but since it's a shallow copy operation it only works for one level (e.g. a dict of dict of DescID will fail).

      For more information about the difference please read Shallow vs Deep Copying of Python Objects.

      However here a couple methods to copy a dict.

      import copy
      dict1 = {'1':dId_one,'2':dId_two}
      dict2 =  copy.copy(dict1)
      
      dict1 = {'1':dId_one,'2':dId_two}
      dict2 =  dict1.copy()
      
      dict1 = {'1':dId_one,'2':dId_two}
      dict2 =  dict(dict1)
      

      Cheers,
      Maxime.

      posted in General Talk
      M
      m_adam
    • RE: Python Script Gui: Use EditSlider Get Float

      Hi @mike, first of all I would like to remind you to read and use the Q&A functionnality.

      Regarding your question, here is an example of a basic GeDialog, which make use to SetFloat to define a range.

      import c4d
      
      class MonDlg( c4d.gui.GeDialog):
          idSlider = 1000
          idButton = 1001
      
          # Create the Layout
          def CreateLayout(self):
              self.AddEditSlider(self.idSlider, c4d.BFH_SCALE|c4d.BFV_SCALE, initw=100, inith=20)
              self.AddButton(self.idButton, c4d.BFH_SCALE|c4d.BFV_SCALE, initw=100, inith=20,name = 'Get Value')	
              return True
      
          # Called after CreateLayout
          def InitValues(self):
              self.SetFloat(self.idSlider, 0.25, min=0.0, max=1.0, step=0.01, min2=0.0, max2=0.0)
              return True
      
          # Called for each interaction from a widget
          def Command(self, id, msg):
              if id == self.idButton:
                  print self.GetFloat(self.idSlider)
              
              return True
      
      def main():
          dlg = MonDlg()
          dlg.Open(c4d.DLG_TYPE_MODAL)
      
      if __name__=='__main__':
          main()
      

      If you have any questions please let me know.
      Cheers,
      Maxime!

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: restore weight to BaseContainer and get it Vs Direct get weight from weight Tag

      Hi @chuanzhen actually the problem interested me and I took some personal time on my weekend to do some tests.

      I did 2 test scenes, which was a cube with 163970 points driven by 3 joints
      With your original source code 0.53 fps
      optimize python: 1.09 fps (x2.05)
      C++: 19.36 fps (x36.52)

      And the other one was the same cube but driven by 11 joints.
      With your original source code 0.37 fps
      optimize python: 0.64 fps (x1.73)
      C++: 12.08 (x32.65)

      So here is the optimized python code. As you can see I try to avoid as much as possible doing operation in the nested loop.

      class newskin(plugins.ObjectData):
       
          lasttagdirty = None   #for weight change update
          jointdir = None       #for joint change update
          cachedir = None       #for other bindmesh check
       
          def CheckDirty(self, op, doc):
              #check first run
              if op[c4d.NEWSKINTAG] is None:
                  op.SetDirty(c4d.DIRTYFLAGS_DATA)
                  return
       
              #check weight change
              if op[c4d.NEWSKINTAG].GetDirty(c4d.DIRTYFLAGS_DATA) != self.lasttagdirty:
                  op.SetDirty(c4d.DIRTYFLAGS_DATA)
                  return
       
              #check matrix change
              checktime = op[c4d.NEWSKINTAG].GetJointCount()
              cdirty = 0
       
              for i in xrange(checktime):
                  cdirty += op[c4d.NEWSKINTAG].GetJoint(i).GetDirty(c4d.DIRTYFLAGS_MATRIX)
       
              if cdirty != self.jointdir or self.cachedir != op[c4d.NEWSKINCHECK].GetDirty(c4d.DIRTYFLAGS_CACHE):
                  op.SetDirty(c4d.DIRTYFLAGS_DATA)
                  return
       
              return
       
         
          def ModifyObject(self, mod, doc, op, op_mg, mod_mg, lod, flags, thread):
       
              if mod[c4d.NEWSKINTAG] is None or mod[c4d.NEWSKINCHECK] is None:
                  return True
       
              tag = mod[c4d.NEWSKINTAG]
              self.lasttagdirty = tag.GetDirty(c4d.DIRTYFLAGS_DATA)  #get current weight tag dirty
              self.cachedir = mod[c4d.NEWSKINCHECK].GetDirty(c4d.DIRTYFLAGS_CACHE)    #get bindmesh check
              plist = [pos * op_mg for pos in op.GetAllPoints()]
              pcount = op.GetPointCount()  
              jcount = tag.GetJointCount()
              self.jointdir = 0
       
              for m in xrange(jcount):
                  joint = tag.GetJoint(m)
                  self.jointdir += joint.GetDirty(c4d.DIRTYFLAGS_MATRIX)   #all joint current matrix dirtycount
       
              temp = c4d.Vector()
              for n in xrange(pcount):    #n:point index  
                  temp %= temp
       
                  for m in xrange(jcount):
                      joint = tag.GetJoint(m)
                      weight = tag.GetWeight(m, n)
       
                      if not weight :
                          continue  
       
                      cjmg = joint.GetMg()
                      jdict = tag.GetJointRestState(m)
                      jmg = jdict["m_bMg"]
                      jmi = jdict["m_bMi"]
       
                      temp += weight * cjmg * jmi * plist[n]  #defrmer global pos
                  plist[n] = temp
             
              plist = [~op_mg * pos for pos in plist]
              op.SetAllPoints(plist)
              op.Message(c4d.MSG_UPDATE)
       
              return True
      

      And here the C++ version (compatible R20 only). It uses paralellFor, not sure it's really worth didn't do proper profiling about it but I wanted to try them. Moreover please do not consider my C++ code has fully optimized since you could probably improve it depending on the situation.

      #include "c4d_symbols.h"
      #include "main.h"
      #include "c4d_objectdata.h"
      #include "lib_ca.h"
      
      // Local resources
      #include "oskinmodifier.h"
      #include "maxon/parallelfor.h"
      #include "maxon/basearray.h"
      
      /**A unique plugin ID. You must obtain this from http://www.plugincafe.com. Use this ID to create new instances of this object.*/
      static const Int32 ID_OBJECTDATA_SKINMODIFIER = 1000002;
      
      class SkinModifier : public ObjectData
      {
      	INSTANCEOF(SkinModifier, ObjectData)
      
      public:
      	static NodeData* Alloc() { return NewObj(SkinModifier) iferr_ignore("SkinModifier plugin not instanced"); }
      
      	virtual void CheckDirty(BaseObject *op, BaseDocument *doc);
      	virtual Bool ModifyObject(BaseObject* mod, BaseDocument* doc, BaseObject* op, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread);
      
      private:
      
      	BaseList2D* GetBaseLink(BaseObject* op, DescID id, Int excepted);
      	Int lasttagdirty; /// get current weight tag dirty
      	Int jointdir;
      	Int cachedir;
      };
      
      void SkinModifier::CheckDirty(BaseObject *op, BaseDocument *doc)
      {
      	BaseList2D* t = GetBaseLink(op, DescID(NEWSKINTAG), Tweights);
      	BaseList2D* l = GetBaseLink(op, DescID(NEWSKINCHECK), Opolygon);
      	if (!t || !l)
      		return;
      
      	PointObject* linkOp = static_cast<PointObject*>(l);
      	CAWeightTag* tag = static_cast<CAWeightTag*>(t);
      
      	// check first run
      	if (!tag)
      	{
      		op->SetDirty(DIRTYFLAGS::DATA);
      		return;
      	}
      
      	// check weight change
      	if (tag->GetDirty(DIRTYFLAGS::DATA) != lasttagdirty)
      	{
      		op->SetDirty(DIRTYFLAGS::DATA);
      		return;
      	}
      
      	// check matrix change
      	Int cdirty = 0;
      	for (Int i = 0; i < tag->GetJointCount(); i++)
      	{
      		cdirty += tag->GetJoint(i, tag->GetDocument())->GetDirty(DIRTYFLAGS::MATRIX);
      		if (cdirty != jointdir || cachedir != linkOp->GetDirty(DIRTYFLAGS::CACHE))
      		{
      			op->SetDirty(DIRTYFLAGS::DATA);
      			return;
      		}
      	}
      
      	return;
      }
      
      BaseList2D* SkinModifier::GetBaseLink(BaseObject* op, DescID id, Int excepted)
      {
      	GeData data;
      	if (!op->GetParameter(id, data, DESCFLAGS_GET::NONE))
      		return nullptr;
      
      	return data.GetLink(op->GetDocument(), excepted);
      }
      
      
      Bool SkinModifier::ModifyObject(BaseObject* mod, BaseDocument* doc, BaseObject* op, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread)
      {
      	if (!mod || !op || !doc || !thread)
      		return false;
      
      	BaseList2D* t = GetBaseLink(mod, DescID(NEWSKINTAG), Tweights);
      	BaseList2D* l = GetBaseLink(mod, DescID(NEWSKINCHECK), Opolygon);
      	if (!t || !l)
      		return false;
      
      	PointObject* linkOp = static_cast<PointObject*>(l);
      	CAWeightTag* tag = static_cast<CAWeightTag*>(t);
      
      	lasttagdirty = tag->GetDirty(DIRTYFLAGS::DATA);
      	cachedir = linkOp->GetDirty(DIRTYFLAGS::CACHE);
      
      	PointObject* pObj = ToPoint(op);
      	const Vector * pListR = pObj->GetPointR();
      	Vector * pListW = pObj->GetPointW();
      
      	const Int pcount = pObj->GetPointCount();
      	const Int jcount = tag->GetJointCount();
      	jointdir = 0;
      
      	for (Int i = 0; i < jcount; i++)
      	{
      		BaseObject* joint = tag->GetJoint(i, doc);
      		jointdir += joint->GetDirty(DIRTYFLAGS::MATRIX);
      	}
      
      
      	auto worker = [jcount, op_mg, &pListR, &pListW, &doc, &tag](maxon::Int i)
      	{
      		Vector temp;
      		Vector pos = op_mg * pListR[i];
      
      		for (Int m = 0; m < jcount; m++)
      		{
      			Vector pos = op_mg * pListR[i];
      			BaseObject* joint = tag->GetJoint(m, doc);
      			Float weight = tag->GetWeight(m, i);
      
      			if (weight == 0.0)
      				continue;
      
      			Matrix cjmg = joint->GetMg();
      			JointRestState jrest = tag->GetJointRestState(m);
      			Matrix jmg = jrest.m_bMg;
      			Matrix jmi = jrest.m_bMi;
      
      			temp += weight * cjmg * jmi * pos;
      		}
      		pListW[i] = ~op_mg * temp;
      	};
      
      	maxon::ParallelFor::Dynamic(0, pcount, worker);
      
      	// Notify Cinema about the internal data update.
      	op->Message(MSG_UPDATE);
      
      	return true;
      }
      
      Bool RegisterSkinModifier()
      {
      	return RegisterObjectPlugin(ID_OBJECTDATA_SKINMODIFIER, "C++ oskinmodifier"_s, OBJECT_MODIFIER, SkinModifier::Alloc, "oskinmodifier"_s, nullptr, 0);
      }
      

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Adding items to a LONG/CYCLE description container.

      Hi @FlavioDiniz, first of all, welcome in the plugincafe community!

      Don't worry, since it's your first topic, I've set up your topic correctly. But please for your next topics, use the Q&A functionnaly, make sure to also read How to Post Questions.

      With that's said you can find the manual about SetDParameter in the C++ docs which will be trigger each time a user changes a value in the description of your tag. And also the C++ manual about GetDDescription.
      Here a quick example of the implementation in python

          extensions = ["exr","hdr","jpg","png"]
      
          # Called by C4D when loading description
          def GetDDescription(self, node, description, flags):
              data = node.GetDataInstance()
              if data is None:
                  return False
      
              # Load the description
              if not description.LoadDescription(node.GetType()):
                  return False
      
              # If there is no description for c4d.TESTSCALE, we create it. Otherwise we override it and set DESC_CYCLE with our extensions list.
              singleID = description.GetSingleDescID()
              paramID = c4d.DescID(c4d.TESTSCALE)
              if singleID is None or paramID.IsPartOf(singleID)[0]:
                  bcParam = description.GetParameterI(c4d.TESTSCALE)
      
                  items = c4d.BaseContainer()
                  for i, extension in enumerate(self.extensions):
                      items.SetString(i, extension)
      
                  bcParam[c4d.DESC_CYCLE] = items
      
              return (True, flags | c4d.DESCFLAGS_DESC_LOADED)
      
          # Called when a parameter is Set
          def SetDParameter(self, node, id, t_data, flags):
              if not node:
                  return
      
              # called when the filename is changed. We add the path to our list and we return c4d.DESCFLAGS_PARAM_SET to inform c4d the paramaters is just sets
              if id[0].id == c4d.STRINGTEST:
                  self.extensions.append(t_data)
                  return (True, flags | c4d.DESCFLAGS_PARAM_SET)
      
              return False
      

      If you have any questions, please let me know!
      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Get all texture tags & show texture paths,but reflection channel throw error? :(

      Hi @Ashton_FCS_PluginDev, we are glad to see you back in the new plugincafe community!
      No worries at all since it's your first post here, but please make sure to read and apply the rules defined on the following topics:

      • How to Post Questions.
      • Q&A Functionality.

      Regarding your issue, you read m_spec, but you never check if there is a bitmashader in it or not. And in your case, there is no, so m_spec is set to None, and you are then trying to access value.
      So please always check for None.
      Then since you directly read the BITMAPSHADER_FILENAME parameter from this shader, please also consider to check the shader type you retrieve, maybe a user used a noise and not a bitmap.

      m_spec = userMat[c4d.MATERIAL_REFLECTION_SHADER]
      if not m_spec or m_spec.CheckType(c4d.Xbitmap):
          continue
      

      Finally, as you may already be aware, reflection gets multiple layers. You get some information in this thread, how to deal with it.

      If you have any others question, please let me know!
      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: deformer object priority

      Hi @mike,
      No worries at all since, but please make sure to read and apply the rules defined on the following topics:

      • Q&A Functionality.

      Regarding your question, you can make use of the Shift Priority Tags. You can find more information on the priority in the c4d help.

      But C4D also offer the Shift Priority Tag.
      Finally, note in some case it can be a limitation of the viewport which not refresh properly while data are correctly set.
      And you need to manually refresh the viewport.

      Hope it solves your issue, if you have any question please let me know!
      Cheers,
      Maxime!

      posted in Cinema 4D SDK
      M
      m_adam

    Latest posts made by m_adam

    • RE: Edge ring selection

      Hi @Tpaxep,

      Welcome to the Maxon developers forum and its community, it is great to have you with us!

      Getting Started

      Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

      • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
      • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
      • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

      It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.

      About your First Question

      The tool has indeed been updated, but the docs were not.

      Here is how to call it. Note that you have to pass a polygon index, this is mandatory and it needs to be adjacent to one of the vertex to indicate a direction of the ring selection. Here is your script adapted, to be run on a sphere that was made editable.

      import c4d
      from c4d import utils
      
      def select_ring_edge(obj, v1Id, v2Id, polyId):
          bc = c4d.BaseContainer()
          bc.SetData(c4d.MDATA_RING_SEL_STOP_AT_SELECTIONS, False)
          bc.SetData(c4d.MDATA_RING_SEL_STOP_AT_NON_QUADS, False)
          bc.SetData(c4d.MDATA_RING_SEL_STOP_AT_POLES, True)
          bc.SetData(c4d.MDATA_RING_BOTH_SIDES, False)
          bc.SetData(c4d.MDATA_RING_SWAP_SIDES, False)
          
          bc.SetData(c4d.MDATA_RING_FIRST_VERTEX, v1Id)
          bc.SetData(c4d.MDATA_RING_SECOND_VERTEX, v2Id)
          bc.SetData(c4d.MDATA_RING_POLYGON_INDEX, polyId)
          
          
          bc.SetData(c4d.MDATA_RING_SELECTION, c4d.SELECTION_NEW)
      
          result = c4d.utils.SendModelingCommand(
              command=c4d.ID_MODELING_RING_TOOL,
              list=[obj],
              mode=c4d.MODELINGCOMMANDMODE_EDGESELECTION,
              bc=bc,
              doc=c4d.documents.GetActiveDocument(),
              flags=c4d.MODELINGCOMMANDFLAGS_NONE
          )
          c4d.EventAdd()
          return result
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          obj = doc.GetActiveObject()
          if obj is None:
              c4d.gui.MessageDialog("select obj.")
              return
      
          firstVertex = 346
          secondVertex = 347
          polygonIndex = 312
      
          result = select_ring_edge(obj, firstVertex, secondVertex, polygonIndex)
          if result:
              print("DONE")
          else:
              print("ERROR")
      
      if __name__ == '__main__':
          main()
      

      Note that the same settings also apply for the ID_MODELING_LOOP_TOOL, just modify RING by LOOP in the constant so MDATA_RING_SEL_STOP_AT_SELECTIONS become MDATA_LOOP_SEL_STOP_AT_SELECTIONS.

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: How to implement an image control that can receive and generate image data drag events

      Did you had a look at geuserarea_drag_r13.py? There is no else statement but that would be the perfect place to do something specific on single click. But there is no built-in solution you have to kind of track all by yourself as done line 294.

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: adding assets to a userdatabase in the assetbrower with Python

      Hi @wen I've moved your topic to the bug section since it's indeed a bug, I will ping you on this topic once the fix is available, it should come in one of the next update.
      The issue is that the internal cache is not properly updated and therefor this is failing.

      With that's said there is a ugly workaround which consist of calling it twice so the cache is properly updated.
      Find bellow a version that is going to work in all versions

      import c4d
      import maxon
      import os
      
      
      def CreateRepFromUrl(url: maxon.Url) -> maxon.UpdatableAssetRepositoryRef:
          """Create a new repository from a given database URL.
          
          If there is no valid database at the given URL, it creates a database at the URL.
          It always create a new repository and the associated database asset, even if there
          are existing repositories for that database.
          """
          # Make type checks
          if not isinstance(url, maxon.Url):
              raise TypeError("First argument is not a maxon.Url")
      
          # Create a unique identifier for the repository.
          rid = maxon.AssetInterface.MakeUuid(str(url), True)
      
          # Repositories can be composed out of other repositories which are called bases. In this
          # case no bases are used to construct the repository. But with bases a repository for all
          # user databases could be constructed for example.
          bases = maxon.BaseArray(maxon.AssetRepositoryRef)
      
          # Create a writable and persistent repository for the database URL. If #_dbUrl would point
          # to a location where no database has been yet stored, the necessary data would be created.
          if c4d.GetC4DVersion() < 2025200:
              try:
                  repository = maxon.AssetInterface.CreateRepositoryFromUrl(
                      rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False, None)    
              except Exception as e:
                  repository = maxon.AssetInterface.CreateRepositoryFromUrl(
                      rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False, None)    
          else:
              try:
                  repository = maxon.AssetInterface.CreateRepositoryFromUrl(
                      rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False)    
              except Exception as e:
                  repository = maxon.AssetInterface.CreateRepositoryFromUrl(
                      rid, maxon.AssetRepositoryTypes.AssetDatabase(), bases, url, True, False, False)    
      
          if not repository:
              raise RuntimeError("Repository construction failed.")
      
          return repository
      
      if __name__ == '__main__':
          if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading():
              raise RuntimeError("Could not load asset databases.")
          dbPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdb")
          print(CreateRepFromUrl(maxon.Url(dbPath)))
      

      Cheers,
      Maxime.

      posted in Bugs
      M
      m_adam
    • RE: plugin Loupedeck CT with Redshift

      Hi @shafy sorry for the delay I was away for few times.

      Before we start please read the Support Procedure - About AI-Supported Development. Keep in mind we are not there to develop for you neither integrate or guide you with 3rd party module that you will most likely need in order to control knobs.

      With that's said what you are asking (at least retrieving a parameter and defining it) is possible for Redshift light intensity. It is as easy as drag and dropping the parameter you are interested by into the Cinema 4D Python Console as explained in Python Script Manager Manual - Drag And Drop. Additionally you can also use the Script Log to record a script or change and therefor re-execute it.

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: How to expose C++ symbols to Python users as a third party?

      Hi @ECHekman sorry it took me more time than I expected. From my meory octane already have a Python module registered in C++. So I assume you want to add symbols to this python module.

      One important aspect is that the Symbol Parser by itself is completly agnostic of the c4d module therefor you can call it with any Python interpreter. This point is really important because this let you parse your C++ header files whenever you want within your build pipeline.

      With that's said the Symbol Parser is bundled within the mxutils package which needs the c4d and maxon module.
      So in order to have it running with any python intepreter you will need to do the following:

      import sys
      CI_PROJECT_DIR = r"C:\Users\m_adam.INTERN\Documents\MAXON\gitlab\c4d"
      
      # Build path to import the parser_symbol
      maxon_python_path = os.path.join(CI_PROJECT_DIR, "resource", "modules", "python", "libs")
      if not os.path.exists(maxon_python_path):
          raise RuntimeError(f"DEBUG: update_python_symbols - Unable to find {maxon_python_path}")
      
      # Get python311 folder where the symbol parser is located
      mxutils_path, symbol_parser_path = None, None
      for folder in os.listdir(maxon_python_path):
          full_path_folder = os.path.join(maxon_python_path, folder)
          if not os.path.isdir(full_path_folder):
              continue
      
          # We found the mxutils module containing the symbol parser.
          if os.path.exists(os.path.join(full_path_folder, "mxutils", "symbol_parser")):
              mxutils_path = os.path.join(full_path_folder, "mxutils")
              symbol_parser_path = os.path.join(mxutils_path, "symbol_parser")
      
      if None in (mxutils_path, symbol_parser_path):
          raise ImportError(f"Could not find 'symbol_parser' module path in {maxon_python_path}.")
      
      sys.path.append(mxutils_path)
      print(f"DEBUG: Added {mxutils_path} to sys.path.")
      
      # Import the symbol parser and do your stuff.
      import symbol_parser
      # Do something with it
      
      sys.path.remove(mxutils_path)
      

      Then the Symbol Parser paradigm is that you first parse your data and then the parser contain Scope that contains member (a name and a value).
      Therefor once you have parsed your data you can freely output to multiple format if needed. You can also write your own format if you want to.
      In this case because you are using mostly the Cinema API and not the Maxon API I re-use the same output as the one we use for the c4d Python package.
      This output is implemented in c4d\resource\modules\python\libs\python311\mxutils\symbol_parser\output_classic_api.py. Everything in the Symbol Parser is open source so feel free to look at it.

      So to get back to your question find bellow the next code that will parse all the resources files from the Redshift folder and insert them within the "redshift" Python Package. (You usually do not have to do that because Python is parsing automatically the c4d_symbols.h and include such symbols within the c4d Python package this is for the example). With that's said there is basically two ways, one hardcoding the parsed value in your C++ plugin when you register your Python module, the second one using a Python file that will get loaded at the startup of Cinema 4D once your Python module is already loaded and therefor inject symbol into your module.

      import os
      import c4d # Only used to retrieve various C4D paths used in this example, but not really necessary otherwise
      
      # These imports bellow are not bound to the c4d module therefor they can be used with a standard Python3 interpreter
      # But this code is written to be executed in the Script Manager, so you may need to adapt the import statement according to how you run this script.
      from mxutils.symbol_parser.extractor import SymbolParser
      from mxutils.symbol_parser.output_classic_api import _get_symbol_dict_from_parser
      
      
      def ParseRedshiftResources() -> SymbolParser:
          # Parse all Redshift ressource files and resolve their values
          rsDir = os.path.join(os.path.dirname(c4d.storage.GeGetStartupApplication()), "Redshift", "res", "description")
          parser = SymbolParser(rsDir,
                                  parse_mx_attribute=False,
                                  parse_define=True,
                                  parse_enum=True,
                                  parse_static_const=True)
      
          # Parse and resolve. Resolve means:
          #  - Transfrom values that depends to others to their actual integer or float values.
          #  - Transform complex values (such as bit mask, arithmetic, etc) into their actual integer or float values.
          # For more information about what is supported take a look at
          # https://developers.maxon.net/docs/py/2025_2_0/manuals/manual_py_symbols.html?highlight=symbol%20parser#symbols-parser-features
          parser.parse_all_files(resolve=True)
          return parser
      
      
      def OutputPythonFile(parser: SymbolParser):
          def GeneratePythonFileToBeInjected(parser: SymbolParser) -> str:
              """Generate a Python file that will inject all parsed symbols into the 'redshift' Python package when this file get imported.
              This function needs to be called once, most likely during your build pipeline.
              Once you have generated this Python file you should bundle it with your installer and then install it in the targeted Cinema 4D installation.
              See PatchCurrentC4DToInjectParsedSymbol for an example of such deployment
              """
              import tempfile
      
              tempPath = None
              # Retrieve a sorted dict containing the symbol name as key and it's value as values
              # This will flatten all scopes so an enums called SOMETHING with a value FOO in it will result in a symbol named SOMETHING_FOO
              # If you do not want this behavior feel free to create your own solution, this function is public and declared in
              # c4d\resource\modules\Python\libs\python311\mxutils\symbol_parser\output_classic_api.py
              symbolTable = _get_symbol_dict_from_parser(parser)
              
              with tempfile.NamedTemporaryFile('w', delete=False) as tmpFile:
                  tempPath = tmpFile.name
                  
                  tmpFile.write("import redshift\n")
                  
                  for name, value in symbolTable.items():
                      tmpFile.write(f"redshift.{name} = {value}\n")
                  
              
              if not os.path.exists(tempPath):
                  raise RunTimeError('Failed to create the Python File to Inject Symbols in "redshift" Python Package') 
              
              return tempPath
      
          def PatchCurrentC4DToInjectParsedSymbol(fileToBeLoaded: str):
              """This function is going to create a Python file that is going to be called at each startup of Cinema 4D to inject symbols into the "redshift" Python package.
              This is done by placing the previous file in a place where it can be imported by the C4D Python VM.
              And by importing this file by editing the python_init.py file located in the preferences.
              For more information about it please read 
              https://developers.maxon.net/docs/py/2025_2_0/manuals/manual_py_libraries.html?highlight=librarie#executing-code-with-Python-init-py
              """
              import sys
              import shutil
              
              # Pref folder that contains a python_init.py that is laoded once c4d and maxon package is loaded and a libs folder that is part of the sys.path of the c4d Python VM.
              pyTempDirPath = os.path.join(c4d.storage.GeGetStartupWritePath(), f"Python{sys.version_info.major}{sys.version_info.minor}")
          
              # Path to our module that will inject the parsed symbols in the "redshift" Python package
              pyTempRsDirPath = os.path.join(pyTempDirPath, "libs", "rs_symbols")
              # Path to the __init__ file of our module that will be called when we import our module
              # we need to place the file we generated previously in this location.
              pyTempRsFilePath = os.path.join(pyTempDirPath, "libs", "rs_symbols", "__init__.py")
              if not os.path.exists(pyTempRsDirPath):
                  os.mkdir(pyTempRsDirPath)
                  
              if os.path.exists(pyTempRsFilePath):
                  os.remove(pyTempRsFilePath)
                  
              shutil.copy2(fileToBeLoaded, pyTempRsFilePath)
              
              # Now that we have our module that will inject symbols within the "redshift" Python package. We need to get this "rs_symbols" module be called at each startup.
              # So we use the python_init.py file to get loaded once all plugins are loaded. Therefor the Redshift plugin is already loaded and have already setup its "redshift" Python package.
              # We will inject a line in this file to load our "rs_symbols" module
              pyTempInitFilePath = os.path.join(pyTempDirPath, "python_init.py")
              
              isImportRsSymbolPresent = False
              initFileExist = os.path.exists(pyTempInitFilePath)
              # Because this file me be already present and already patched or contain other content
              # We should first check if we need to add our import or not
              if initFileExist:
                  with open(pyTempInitFilePath, 'r') as f:
                      isImportRsSymbolPresent = "import rs_symbols" in f.read()
              
              # prepend our import statement to the file
              if not isImportRsSymbolPresent:
                  with open(pyTempInitFilePath, "w+") as f:
                      content = f.read()
                      f.seek(0, 0)
                      f.write('import rs_symbols\n' + content)
      
          pythonFileGeneratedPath = GeneratePythonFileToBeInjected(parser)
          PatchCurrentC4DToInjectParsedSymbol(pythonFileGeneratedPath)
      
      
      def OutputCppFile(parser):
          # Retrieve a sorted dict containing the symbol name as key and it's value as values
          # This will flatten all scope so an enums called SOMETHING with a value FOO in it will result in a symbol named SOMETHING_FOO
          # If you do not want this behavior feel free to create your own solution, this function is public and declared in
          # c4d\resource\modules\Python\libs\python311\mxutils\symbol_parser\output_classic_api.py
          symbolTable = _get_symbol_dict_from_parser(parser)
          
          contentToPaste =  'auto mod_rs = lib.CPyImport_ImportModule("redshift");\n'
          contentToPaste += 'auto modDict = lib.CPyModule_GetDict(mod_rs);\n'
              
          for name, value in symbolTable.items():
              contentToPaste += f'\nauto name = lib.CPyUnicode_FromString("{name}");\n'
              contentToPaste += 'if (!name)\n'
              contentToPaste += '  CPyErr_Print();\n'
              contentToPaste += f'maxon::py::CPyRef value = lib.CPyLong_FromInt32({value});\n'
              contentToPaste += 'if (!value)\n'
              contentToPaste += '  CPyErr_Print();\n'
              contentToPaste += 'if (!lib.CPyDict_SetItem(modDict, name, value))\n'
              contentToPaste += '  CPyErr_Print();\n'
                      
          return contentToPaste
      
      
      def main() -> None:
          # Parse the Redshift resources and return the parser that hold the parsed values
          parser = ParseRedshiftResources()
      
          # First way: Generate a file that patch the current C4D with a Python file that you need to deploy on the installer. You may prefer this option since you can update this file without having to recompile your plugin.
          OutputPythonFile(parser)
      
          # Second way: Output pseudo C++ code that can be pasted within your C++ plugin after you initialize your custom Python module. You may want to call this whole script before you compile in your build pipeline and put the "cppContent" within a file that will get compiled automatically
          cppContent = OutputCppFile(parser)
      
      if __name__ == '__main__':
          main()
      

      So the system is really modular and being able to run on a regular Python interpreter let you integrate it within your build pipeline to suits your needs.
      If you have any questions, please let me know.
      Cheers,
      Maxime.

      posted in General Talk
      M
      m_adam
    • RE: Move the plane in random effect

      Hi @Fabio-B

      Thank you for reaching out to us. Unfortunately, your question is off topic for the forum you have posted in. Please check the Forum Overview for an overview of the scope of the different forums.

      On this forum we only provide support for third party software developers about the Cinema 4D Software Development Kit. We unfortunately cannot provide end user support, as we lack the tools and the knowledge to do so.

      To receive end-user support, please visit our Support Center and create a ticket for your issue

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: C4D Python: Redshift IPR Start/Stop Control

      @hoganXYZ hi if it does not appear in the script log then there is no clear rules. Sometimes there is another way sometimes now it depends on how it is implemented. For the one you asked I do not think it is possible. Best bet would be to ask Redshift on their forum.

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: How to expose C++ symbols to Python users as a third party?

      Hi just to let you know I will answers you next week Monday.

      Cheers,
      Maxime.

      posted in General Talk
      M
      m_adam
    • RE: lhit from BaseVolumeData

      Hi sadly there is not much you can do with it in Python. The only use case is if your want to create a kind of hashmap on the pointer, but I do not see a big use case of that to be honest. I will see if I can improve thing a bit here, because in C++ via the lhit you can retrieve the polygon and the underlying object being hit, which is important information but this is very low on my priority list. So in general regarding shader I would say you better at look at C++ since in any case doing a Shader in Python will most likely be a performance killer.

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: C4D Python: Redshift IPR Start/Stop Control

      Hi @itstanthony you can use c4d.CallCommand(1040239) to toggle the start/stop of the Redshift IPR.
      You can know such command ID by using the Script Log and doing the action you want to.

      Then to check if the command is active you can use c4d.IsCommandChecked(1040239).

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam