restore weight to BaseContainer and get it Vs Direct get weight from weight Tag
-
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? -
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! -
@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 ,
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
-
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 callingSetPoint
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. -
@m_adam Thanks ,this really helped me!
-
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. -
@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)