Custom FieldList is not reevaluated on field change
-
Hello,
I am trying to write my effector plugin with a custom fieldlist input. I follow the docs on how to sample this list:GeData ge; if (bc->GetParameter(GUI_ID_FIELDLIST, ge) == false) return ; CustomDataType* const customData = ge.GetCustomDataType(CUSTOMDATATYPE_FIELDLIST); FieldList* const p = static_cast<FieldList*>(customData); if (p) { p->SetFlags(FIELDLIST_FLAGS::DISABLEVALUEBYDEFAULT, true); Int sampleCnt = md->GetCount(); maxon::BaseArray<maxon::Vector> positions; auto _n = positions.Resize(sampleCnt); maxon::BaseArray<maxon::Vector> uvws; _n = uvws.Resize(sampleCnt); maxon::BaseArray<maxon::Vector> directions; _n = directions.Resize(sampleCnt); // set positions Float64 xOffset = 0.0; for (maxon::Vector& pos : positions) { pos.x = xOffset; xOffset += 1.f; } // define points to sample FieldInput points(positions.GetFirst(), directions.GetFirst(), uvws.GetFirst(), sampleCnt, Matrix()); FieldOutput out = p->SampleListSimple(*op, points).GetValue(); if (out.IsValid()) { for (Int it = 0; it < sampleCnt; ++it) print() << out._value[it]; } }
The problem is that effector is not reevaluated if i change any of the field parameters (like seed etc), the rest of params (on the effector) works just as expected.
Is there anything more need to be done to get it working say linking or a dependency generation?
Thanks in advance,
Andrew -
hi @Andrew,
this seems to be the same problem as in this thread
Overriding the functionCheckDirty
should solve your problem.Let me know if it's not or if i misunderstood your problem.
Cheers,
Manuel -
Hi @m_magalhaes ,
Thanks for that tip, it helped me perfectly with my custom Field (fieldData) object where the same problem with FieldList GUI element appear, but in my custom effector (EffectorData) that CheckDirty function never get called at all. For now i use a different workaround, in the InitPoints i do :BaseObject* child = op->GetDown(); while (child != nullptr) { this->AddEffectorDependence (child); child = child->GetNext(); }
Not sure how safe it is in terms of cyclic dependencies/multiple registrations, but works stable so far. Let me know if that is dangerous.
Much thanks,
Andrew -
hi,
this doesn't seem to be the right way to do it. If the fields object isn't a child of your effector, this will not work.
Our DropEffector example wasn't working with fields (only falloff) so I've updated it.
I'm not sure where you are adding your code so i'm not sure why it's not working.Here's the updated version of the drop effector. Could you try to compile it on your side and tell me if it's working or not. If not, how to reproduce the issue
// this example demonstrates how to implement a more complex direct control effector // and utilize falloff and strength directly // the effector drops the particles to a surface #include "c4d.h" #include "c4d_symbols.h" #include "lib_collider.h" #include "c4d_baseeffectorplugin.h" #include "c4d_falloffplugin.h" #include "oedrop.h" #include "main.h" struct DropEffectorData { BaseObject* target; Int32 mode; Float maxdist; Matrix genmg, igenmg; Matrix targmg, itargmg; }; class DropEffector : public EffectorData { public: DropEffectorData ed; AutoAlloc<GeRayCollider> rcol; GeRayColResult rcolres; virtual Bool InitEffector(GeListNode* node); virtual void InitPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread); virtual void ModifyPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread); static NodeData* Alloc() { return NewObjClear(DropEffector); } }; Bool DropEffector::InitEffector(GeListNode* node) { if (!rcol || !node) return false; BaseObject* op = (BaseObject*)node; if (!op) return false; BaseContainer* bc = op->GetDataInstance(); if (!bc) return false; bc->SetFloat(DROPEFFECTOR_DISTANCE, 1000.0); return true; } void DropEffector::InitPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread) { BaseContainer* bc = op->GetDataInstance(); if (!bc) return; if (!rcol) return; ed.mode = bc->GetInt32(DROPEFFECTOR_MODE); ed.maxdist = bc->GetFloat(DROPEFFECTOR_DISTANCE); ed.target = bc->GetObjectLink(DROPEFFECTOR_TARGET, doc); if (!ed.target) return; ed.targmg = ed.target->GetMg(); ed.itargmg = ~ed.targmg; ed.genmg = gen->GetMg(); ed.igenmg = ~ed.genmg; // Add a dependency so that the effector will update if the target changes AddEffectorDependence(ed.target); // Can't init raycollider or the target isn't polygonal, then skip if (!rcol->Init(ed.target)) ed.target = nullptr; else if (!ed.target->IsInstanceOf(Opolygon)) ed.target = nullptr; } void DropEffector::ModifyPoints(BaseObject* op, BaseObject* gen, BaseDocument* doc, EffectorDataStruct* data, MoData* md, BaseThread* thread) { if (!ed.target || !rcol || data->strength == 0.0) return; C4D_Falloff* falloff = GetFalloff(); FieldOutput* fieldSample = nullptr; if (!falloff) { fieldSample = CalcFields(doc, gen, md, FIELDSAMPLE_FLAG::VALUE); if (!fieldSample) return; } Int32 i = 0; Float fall = 0.0; Vector off = Vector(0.0); Vector ray_p = Vector(0.0), ray_dir = Vector(0.0); Vector targ_off = Vector(0.0), targ_hpb = Vector(0.0); MDArray<Int32> flag_array = md->GetLongArray(MODATA_FLAGS); MDArray<Matrix> mat_array = md->GetMatrixArray(MODATA_MATRIX); MDArray<Float> weight_array = md->GetRealArray(MODATA_WEIGHT); if (!mat_array) return; Int32 mdcount = (Int32)md->GetCount(); for (i = 0; i < mdcount; i++) { // If the particle isn't visible, don't calculate if (!(flag_array[i] & MOGENFLAG_CLONE_ON) || (flag_array[i] & MOGENFLAG_DISABLE)) continue; // Multiply into global space off = mat_array[i].off; off = ed.genmg * off; // Sample the falloff if (falloff) { falloff->Sample(off, &fall, true, weight_array[i]); } else { fall = fieldSample->_value[i]; } if (fall == 0.0) continue; // Set up the ray for the collision ray_p = ed.itargmg * off; switch (ed.mode) { default: case DROPEFFECTOR_MODE_PNORMAL: ray_dir = ed.genmg.sqmat * mat_array[i].sqmat.v3; break; case DROPEFFECTOR_MODE_NNORMAL: ray_dir = -(ed.genmg.sqmat * mat_array[i].sqmat.v3); break; case DROPEFFECTOR_MODE_AXIS: ray_dir = (ed.targmg.off - off); break; case DROPEFFECTOR_MODE_SELFAXIS: ray_dir = (ed.genmg.off - off); break; case DROPEFFECTOR_MODE_PX: ray_dir = Vector(1.0, 0.0, 0.0); break; case DROPEFFECTOR_MODE_PY: ray_dir = Vector(0.0, 1.0, 0.0); break; case DROPEFFECTOR_MODE_PZ: ray_dir = Vector(0.0, 0.0, 1.0); break; case DROPEFFECTOR_MODE_NX: ray_dir = Vector(-1.0, 0.0, 0.0); break; case DROPEFFECTOR_MODE_NY: ray_dir = Vector(0.0, -1.0, 0.0); break; case DROPEFFECTOR_MODE_NZ: ray_dir = Vector(0.0, 0.0, -1.0); break; } ray_dir = ed.itargmg.sqmat * ray_dir; // Calculate an intersection if (rcol->Intersect(ray_p, !ray_dir, ed.maxdist, false)) { if (rcol->GetNearestIntersection(&rcolres)) { fall *= data->strength; targ_off = Blend(mat_array[i].off, ed.igenmg * (ed.targmg * rcolres.hitpos), fall); targ_hpb = VectorToHPB(ed.igenmg.sqmat * (ed.targmg.sqmat * rcolres.s_normal)); mat_array[i] = HPBToMatrix(Blend(MatrixToHPB(mat_array[i], ROTATIONORDER::DEFAULT), targ_hpb, fall), ROTATIONORDER::DEFAULT); mat_array[i].off = targ_off; } } } } // be sure to use a unique ID obtained from www.plugincafe.com #define ID_DROPEFFECTOR 1019571 Bool RegisterDropEffector() { return RegisterEffectorPlugin(ID_DROPEFFECTOR, GeLoadString(IDS_DROPEFFECTOR), OBJECT_CALL_ADDEXECUTION, DropEffector::Alloc, "oedrop"_s, AutoBitmap("dropeffector.tif"_s), 0); }
Cheers,
Manuel -
@m_magalhaes
Hi Manuel,sorry for the late reply, the way you sample fields is indeed working but i can't get it working if fields come from my own FIELDLIST gui element. Right now i rely upon the fact that field is always under effector and so if there are no race conditions and cyclic stuff this is acceptable.
thanks,
Andrew -
hi,
this shouldn't make any difference, what do you mean by "my own FIELDLIST gui element" .
Can i see how do you add it and initialise it ?
Cheers,
Manuel