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

    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
    • RE: Getting MoData in GVO of a ObjectData Plugin

      Hi @codysorgenfrey.
      First of all, I would like to present my apologies for the time your topic took to get at least a constructive answer.

      Since matrix object is a bit special MoData are often out of data. It is not an issue for you, here is another solution.
      If you want to read the point position of a matrix, you should convert it into a polygonObject and read Dpoint.
      The con of that is you are losing the Modata (flags, etc, but deformer/effector are still working of course).

      BaseObject* GetVirtualObjects (BaseObject* op, HierarchyHelp* hh)
      {
          // Create a Null master
          BaseObject* outObject = BaseObject::Alloc(Onull);
          if (!outObject)
              return nullptr;
      
          // Get the cache
          BaseObject* matrixObj = op->GetDown();
          if (!matrixObj || !matrixObj->IsInstanceOf(1018545)) // 1018545 = Matrix Object
              return outObject;
      
          // Get the Matrix as a poly, it do not proper touch the Matrix Object, so you have to do it manually
          Bool dirty;
          BaseObject* clone = op->GetAndCheckHierarchyClone(hh, matrixObj, HIERARCHYCLONEFLAGS::ASPOLY, &dirty, nullptr, false);
          if(!dirty)
              return outObject;
      
          // Touch the original matrix in order to hide it in viewport
          matrixObj->Touch();
      
          // Get ptCount and Point Array
          Int32 ptCount = ToPoly(clone)->GetPointCount() / 4;
          const Vector* padr = ToPoint(clone)->GetPointR();
          Dpoint    *d = (Dpoint *) padr;
      
          // iterate over the point and create null at the pt matrix
          maxon::BaseArray<Vector> pointList;
          for(Int32 x = 0; x < ptCount; x++)
          {
              BaseObject* n = BaseObject::Alloc(Onull);
              if(!n)
                  return outObject;
      
              // Get the Correct World Matrix
              Matrix result(d[x].m.off + matrixObj->GetMl().off, d[x].m.sqmat.v1 + matrixObj->GetMl().sqmat.v1, d[x].m.sqmat.v2 + matrixObj->GetMl().sqmat.v2, d[x].m.sqmat.v3 + matrixObj->GetMl().sqmat.v3);
              n->SetMg(result);
              n->InsertUnder(outObject);
      
              pointList.Append(d[x].GetPos());
          }
      
          ApplicationOutput("@", pointList.GetCount());
          return outObject;
      }
      

      Note in this example it only works with 1 matrice object, and as you can see you need to manually touch the object and I assume the result of GetAndCheckHierarchyClone is the result of the Matrix Object itself.
      If you have multiple objects and a matrice is within, it may be worth doing the cache system yourself, please take a look at BaseObject Manual - Generating for more information.

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

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Pipeline integration

      Hi, @knekke first of all welcome in the plugin cafe 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.
      • Developer Support

      I've set up your topic correctly, by marking it as a question adding tags and move it to the correct category.

      With that's said regarding your question, a doc variable is available, in the python_init.py (take care python_init.py is no more loaded in Cinema 4D R20).
      But as you already figured it out, this doc is more or less useless (it can be used to do some stuff), but since python_init.py is called very early, the document is replaced by the new.c4d file afterward.
      So any change made in the doc provided with python_init.py are lost.

      But what you could do is write a PluginMessage plugin and react to C4DPL_PROGRAM_STARTED.
      Here a basic example which adds a cube to the scene.
      Create a *.pyp file in the plugin folder of cinema c4d and past the following code.

      import c4d
      
      def PluginMessage(id, data):
          if id==c4d.C4DPL_PROGRAM_STARTED:
              doc = c4d.documents.GetActiveDocument()
              doc.InsertObject(c4d.BaseObject(c4d.Ocube))
      

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Pipeline integration

      Hi Thomas,

      There is MSG_DOCUMENTINFO. And this message is available in python.

      def Message(id, data):
          if id == c4d.MSG_DOCUMENTINFO:
              if not data: return True
              if data["type"] = c4d.MSG_DOCUMENTINFO_TYPE_LOAD:
                  print "newDoc"
      

      Unfortunately, there is no way to get this message called in C4D, since Python object which supports and receive a Message function are all scene dependants (like Tag, ObjectData).
      In C++ we do have the SceneHook NodeData plugin which allows this kind of stuff since it's a global NodeData, not attached to a special document.

      So your idea is probably the best way to go if you stick to python.
      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: SetDocumentData options for exporting

      Hi @Rage

      I guess export_OBJ example on our Github repository is what you are looking for.
      Note for the moment values of the BaseContainer are not available in the Python Documentation.
      But you could find them in the C++ documentation about fobjexport2.h.

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

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: c4d threading runs only on one core and is slower than single threaded

      Hi @pyr,

      In Python, there is a GIL which do not allow Python by design to execute something in parallel.

      In Cinema 4D all our functions (so everything which is in the c4d python module) is "GIL safe" in order to avoid any issue with Python memory.
      So in Cinema 4D thread are designed for doing GUI, or background stuff.
      Moreover, keep in mind creating a Thread have a cost in term of time (to create, execute, delete them).

      Finally, I would like to point you to multiprocessing vs multi-threading.

      Note that since it's more an algorithm problem than an issue related to our API, we can only provide you hints.

      btw, I also turned your topic as a question. See Q&A New Functionality.
      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Shortcuts for buttons from gui.GeDialog

      Hi @FlavioDiniz, as C4DS said the correct way to handle shortcut is to create a command for them.

      Beside of that in this CommandData.Execute you could do two things:

      • Implement the action (only possible if you don't need any data stored in the GUI, otherwise, your GUI have to expose them in any kind of form).
      • Send a CoreMesage to the dialog, then in your dialog, catch this message and do the logic (The GUI have to be open).

      With that's said, you could also listen for Message BFM_INPUT_DEVICE and especially BFM_INPUT_KEYBOARD. Then react to the keyboard, as it's explained in the Mouse and Keyboard Interaction C++ Manual.
      But in this case, it will not show up in the customize commands window.

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

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Struggling with Python Documentation and pyc4d

      Hi @sungam, first of all, welcome in the plugincafe community and thanks a lot for your feedback. I know these words will not help you but we are currently working to improve our Python Documentation and examples as well.

      With that's said, I know documentation should be sufficient, but MAXON offers free support for developers at plugincafe. So please contact us when you are in trouble, don't know where to look at or even get SDK issue.

      1. res folder is a specific folder where resources needed for your plugin are stored. (e.g. you develop an ObjectData plugin(a new object, like a cube), this cube get a description (a set of parameters, which make a description of the actual object representation), so you need to define theses descriptions within the res folder. Same thing for string and so on...).

      2. Clion is actually a C++ IDE, JetBrains offer Pycharm which is also supported by c4dpy. See C4DPY manual we cover how to setup Pycharm with c4dpy.

      3. "EnvironmentError: cannot find pyp file - plugin registration failed". I'm not sure to understand this, this error comes from c4dpy? Is it possible to get your current plugin, at least the code of your pyp file?

      If you have any questions, please open a new thread, in plugincafe we prefer to have one topic per thread so we can focus only into this specific issue and don't screw conversation, it's also easier for other peoples to find information.

      Do not hesitate to explain to us your project, so we can guide you (help us, to help you 😉 ).
      If it's something you don't want to disclose you can always reach us at [email protected].

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: Spline modeling commands via OBJECT_MODIFIER (Python)

      Hi @merkvilson, just a quick reminder I turned on your topic as a Question, Please See Q&A Functionnality.

      Regarding your issue, C4D does not let you modify a Spline generator. As you may know, all spline generator hold a c4d.LineObject in their cache. And MCOMMAND_DELETE does fail on a LineObject, it only supports PolygonObject or SplineObject.
      So you first need to convert your spline generator as a SplineObject.

      import c4d
      
      def MakeEditableClone(obj):
      	# We clone the obj, since MakeEditable, modify the passed object and we don't want that.
          oDoc = obj.GetClone()
          doc = c4d.documents.BaseDocument()
          doc.InsertObject(oDoc)
          
          res = c4d.utils.SendModelingCommand(
              command = c4d.MCOMMAND_MAKEEDITABLE,
              list = [obj],
              mode = c4d.MODELINGCOMMANDMODE_ALL,
              doc = doc)
          
          if not res:
              return False
          
          return res[0]
      
      def Delete(obj):
          oDoc = obj.GetClone()
          if oDoc.GetDocument() is None:
              doc.InsertObject(oDoc)
      
          oDoc.GetPointS().SelectAll(1)
          
          res = c4d.utils.SendModelingCommand(command  = c4d.MCOMMAND_DELETE,
                                                  list     = [oDoc],
                                                  mode     = 1,
                                                  doc      = doc,
                                                  flags    = 0)
          
          if res == True:
              retObj = oDoc.GetClone()
              oDoc.Remove()
              return retObj
          
          return False
      
      def main():
          splineObj = MakeEditableClone(op)
          if not splineObj: return
          
          finalSpline = Delete(splineObj)
          if not finalSpline: return
          
          doc.InsertObject(finalSpline)
          
          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: GeDialog in a Python Script does not work

      Hi Frank, you actually return True from the Message method while the result of the parent call was expected

      def Message(self, msg, result):
          return c4d.gui.GeDialog.Message(self, msg, result)
      

      For more information please read the documentation about GeDialog.Message.

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam
    • RE: MCOMMAND_JOIN issue

      Hi @merkvilson; MCOMAND_JOIN have changed in R18, now objects have to be under the same hierarchy.
      See Behaviour of MCOMMAND_JOIN different in R18 or [Python] SendModelingCommand().

      Cheers,
      Maxime.

      posted in Cinema 4D SDK
      M
      m_adam