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

    restore weight to BaseContainer and get it Vs Direct get weight from weight Tag

    Cinema 4D SDK
    r19 python
    2
    7
    2.0k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • chuanzhenC
      chuanzhen
      last edited by

      I tried to get the weight from the weight tag, then stored it in the BaseContainer and then applied it to the custom skin deformer, but it was much slower than getting the weight directly from the weight tag. The speed is about one tenth of the latter. What influenced it?
      (In addition, even if I get the weight directly from the weight tag, the fps can only reach half of the c4d skin deformer. )
      Can you have any suggestions?

      相信我,可以的!

      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        Hi @chuanzhen, without any code it's actually hard to help you. Why do you need to store data in a base container?
        As you may already know, a BaseContainer can't store a list, so you have iterate all the joint of the weight tag, then for each join, create a BaseContainer which will hold, the pt_ID and the weight value.
        Then you have to read it again. So it's very inefficient.

        Note if speed is important for you (and it's for any stuff which deals with animation) you should consider switching to c++.
        But without any more information, code I can't really help you more.

        Cheers,
        Maxime!

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        chuanzhenC 1 Reply Last reply Reply Quote 0
        • chuanzhenC
          chuanzhen @m_adam
          last edited by chuanzhen

          @m_adam Thanks for your some suggestions.
          feel that the code is not very readable , so I made a special comment!
          this image is my plugin ,
          0_1539945119657_new_skin.png

          Because of some of my needs, I decided to rewrite a skin.(After working in another skin, in order to ensure the real-time refresh of the window, a "check link box" was set up.)

          this is my code:(Compared to c4d skin it seems to be half its speed)

          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 = 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
          
                  for n in xrange(pcount):    #n:point index   
          
                      pos = op_mg * plist[n]  #global postion
          
                      temp = c4d.Vector()
          
                      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 * pos  #defrmer global pos 
          
                      op.SetPoint(n,~op_mg * temp)
          
                  op.Message(c4d.MSG_UPDATE)
          
                  return True
          

          相信我,可以的!

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by m_adam

            Actually, I don't see any big issue in your code and our role is not to develop for you.
            The only suggestion I can tell is instead of calling SetPoint for each point, maybe it's worth calling directly SetAllPoints.

            Moreover, you could also do a proper profiling by splitting your ModifyObject into separate functions in order to know which part is taking longer and after it's up to you to rework your algorithm. Niklas wrote a great article about that.

            And finally switching to C++ will definitely give you performance improvement. Even with a similar code base.

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            chuanzhenC 1 Reply Last reply Reply Quote 1
            • chuanzhenC
              chuanzhen @m_adam
              last edited by

              @m_adam Thanks ,this really helped me!

              相信我,可以的!

              1 Reply Last reply Reply Quote 0
              • M
                m_adam
                last edited by m_adam

                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.

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                chuanzhenC 1 Reply Last reply Reply Quote 3
                • chuanzhenC
                  chuanzhen @m_adam
                  last edited by

                  @m_adam Wow, I learned some tricks from your code! ( so important to me)
                  Thank you very much for your optimization and testing of your personal time, which is very helpful to me!
                  (C++ is so fast)

                  相信我,可以的!

                  1 Reply Last reply Reply Quote 1
                  • First post
                    Last post