Solved 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 function CheckDirty should solve your problem.

Let me know if it's not or if i misunderstood your problem.

Cheers,
Manuel

MAXON SDK Specialist

MAXON Registered Developer

hi @Andrew,

this seems to be the same problem as in this thread
Overriding the function CheckDirty should solve your problem.

Let me know if it's not or if i misunderstood your problem.

Cheers,
Manuel

MAXON SDK Specialist

MAXON Registered Developer

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

MAXON SDK Specialist

MAXON Registered Developer

@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

MAXON SDK Specialist

MAXON Registered Developer