UNSOLVED Effector Updates

Hello,

I am working on a plugin that uses an In/Exclude list that the user will fill with Effectors and then I will use the new position provided from the Effectors to do other things.

My issue comes in when I initially drop an Effector into the In/Exclude while in R25. The initial values that I get from the Effectors are different than the ones I get from the next time it runs on a refresh.

This isn't an issue in other versions of Cinema because the code runs again immediately with the expected Effector values, meanwhile in R25 it doesn't run a second time until something causes a Cinema refresh which would cause a visual problem in my full plugin because the Effector values are not correct.

Video of the issue in R25

Running same circumstance in R23

I've only run into this issue in R25 with this particular In/Exclude for Effectors, in R25 with an In/Exclude for BaseObjects I haven't had this refresh issue. In R20 through R24 I haven't had this issue with any In/Exclude.

Below is the complete code trimmed down with the problem with the points shown via prints.

#define ID_EFFECTORTEST 1055750

#include "c4d.h"
#include "c4d_gui.h"
#include "c4d_baseeffectordata.h"
#include "customgui_inexclude.h"

class EffectorTestClass : public ObjectData
{
	INSTANCEOF(EffectorTestClass, ObjectData)

public:
	virtual Bool Init(GeListNode* node);
	virtual Bool Message(GeListNode* node, Int32 type, void* data);
	virtual SplineObject* GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt);
	virtual void CheckDirty(BaseObject *op, BaseDocument *doc);
	static NodeData* Alloc() { return NewObjClear(EffectorTestClass); }
	virtual Bool GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags);
	virtual void RunEffectors(BaseObject *op, BaseDocument *doc, PolygonObject *poly);

	maxon::BaseArray<BaseObject*> compareDirtyObj;
	maxon::BaseArray<UInt32> dirtyNumber;

	maxon::Result<void> resultVoid;
	maxon::Result<Int32> resultInt;
	maxon::Result<Vector> resultVector;
	maxon::Result<BaseObject*> resultBaseObject;

	UInt32 opDirtyCount = 0;
};


enum Controls
{
	idMainTab = 100000,
	idEffectorList,

};

Bool EffectorTestClass::Message(GeListNode* node, Int32 type, void* data)
{
	return SUPER::Message(node, type, data);
}

Bool EffectorTestClass::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
{
	BaseContainer *datainstance;
	const DescID *singleid;
	if (!description->LoadDescription(node->GetType()))
	{
	}

	datainstance = ((BaseList2D*)node)->GetDataInstance();
	if (!datainstance)
		return FALSE;
	singleid = description->GetSingleDescID();
	DescID cid;

	DescID DescMainTabGroup = DescLevel(idMainTab, DTYPE_GROUP, 0);
	if (!singleid || DescMainTabGroup.IsPartOf(*singleid, NULL))
	{
		BaseContainer bc;
		bc = GetCustomDataTypeDefault(DTYPE_GROUP);
		bc.SetString(DESC_NAME, "Effector Test"_s);

		bc.SetInt32(DESC_COLUMNS, 1);
		bc.SetInt32(DESC_DEFAULT, 1);
		bc.SetBool(DESC_SCALEH, TRUE);

		if (!description->SetParameter(DescMainTabGroup, bc, DescLevel(ID_EFFECTORTEST)))
			return TRUE;
	}

	cid = DescLevel(idEffectorList, CUSTOMDATATYPE_INEXCLUDE_LIST, 0);
	if (!singleid || cid.IsPartOf(*singleid, NULL))
	{
		BaseContainer bc;
		bc = GetCustomDataTypeDefault(CUSTOMGUI_INEXCLUDE_LIST);

		bc.SetString(DESC_NAME, "Effectors"_s);
		BaseContainer acceptedObjects;
		acceptedObjects.InsData(Obaseeffector, String());
		acceptedObjects.InsData(Oweighteffector, String());

		bc.SetInt32(IN_EXCLUDE_FLAG_INIT_STATE, 3);
		bc.SetInt32(IN_EXCLUDE_FLAG_NUM_FLAGS, 2);
		bc.SetInt32(IN_EXCLUDE_FLAG_IMAGE_01_ON, 300000131);
		bc.SetInt32(IN_EXCLUDE_FLAG_IMAGE_01_OFF, 300000130);
		bc.SetContainer(DESC_ACCEPT, acceptedObjects);
		bc.SetBool(IN_EXCLUDE_FLAG_SEND_SELCHANGE_MSG, true);

		bc.SetInt32(IN_EXCLUDE_FLAG_BIG_MODE_SIZE, 150);
		if (!description->SetParameter(cid, bc, DescMainTabGroup))
			return TRUE;
	}

	flags |= DESCFLAGS_DESC::LOADED | DESCFLAGS_DESC::RECURSIONLOCK;

	return SUPER::GetDDescription(node, description, flags);
}

// Check if the child object, In/Exclude objects have been changed
void EffectorTestClass::CheckDirty(BaseObject *op, BaseDocument *doc)
{
	if (doc == nullptr || op == nullptr)
		return;

	Bool setDirty = FALSE;
	BaseContainer docdata = doc->GetData(DOCUMENTSETTINGS::GENERAL);
	if (docdata == BaseContainer())
		return;

	BaseContainer *dataInstance = op->GetDataInstance();

	maxon::BaseArray<BaseObject*> objArray;

	if (op->GetDown() != nullptr)
		resultBaseObject = objArray.Append(op->GetDown());

	GeData tempdata;
	dataInstance->GetParameter(idEffectorList, tempdata);
	InExcludeData* data = static_cast<InExcludeData*>(tempdata.GetCustomDataType(CUSTOMDATATYPE_INEXCLUDE_LIST));
	if (data != nullptr)
	{
		Int32 flags = 0;
		for (Int32 objIndex = 0; objIndex < data->GetObjectCount(); objIndex++)
		{
			BaseObject* object = static_cast<BaseObject*>(data->ObjectFromIndex(doc, objIndex));
			if (object != nullptr)
			{
				if (object->GetDeformMode())
				{

					flags = data->GetFlags(doc, object);
					if ((flags & 1) == 1)
					{
						resultBaseObject = objArray.Append(object);
					}
				}
			}
		}
	}

	if (Int32(objArray.GetCount()) != Int32(compareDirtyObj.GetCount()))
	{
		setDirty = TRUE;
		dirtyNumber.Reset();
	}

	if (Int32(dirtyNumber.GetCount()) != Int32(objArray.GetCount()))
	{
		for (Int32 objIndex = Int32(dirtyNumber.GetCount()); objIndex < Int32(objArray.GetCount()); objIndex++)
			resultInt = dirtyNumber.Append(0);
	}

	compareDirtyObj.Reset();
	resultVoid = compareDirtyObj.CopyFrom(objArray);

	for (Int32 index = 0; index < Int32(objArray.GetCount()); index++)
	{
		if (dirtyNumber[index] != objArray[index]->GetDirty(DIRTYFLAGS::DATA | DIRTYFLAGS::MATRIX | DIRTYFLAGS::CHILDREN | DIRTYFLAGS::CACHE))
			setDirty = TRUE;

		dirtyNumber[index] = objArray[index]->GetDirty(DIRTYFLAGS::DATA | DIRTYFLAGS::MATRIX | DIRTYFLAGS::CHILDREN | DIRTYFLAGS::CACHE);

	}
	if (setDirty == TRUE || opDirtyCount != op->GetDirty(DIRTYFLAGS::MATRIX | DIRTYFLAGS::DATA))
	{
		opDirtyCount = op->GetDirty(DIRTYFLAGS::MATRIX | DIRTYFLAGS::DATA);
		op->SetDirty(DIRTYFLAGS::DATA | DIRTYFLAGS::CACHE);

	}
}

SplineObject* EffectorTestClass::GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt)
{
	PolygonObject *testObj = (PolygonObject*)op->GetDown();
	if (testObj)
	{
		RunEffectors(op, doc, testObj);
	}
	opDirtyCount = op->GetDirty(DIRTYFLAGS::MATRIX | DIRTYFLAGS::DATA);
	SplineObject * returnSpline = SplineObject::Alloc(0, SPLINETYPE::LINEAR);
	return returnSpline;

}

Bool EffectorTestClass::Init(GeListNode* node)
{
	opDirtyCount = 0;
	return true;
}


void EffectorTestClass::RunEffectors(BaseObject *op, BaseDocument *doc, PolygonObject *poly)
{
	BaseContainer *dataInstance = op->GetDataInstance();
	maxon::BaseArray<Vector> originalCenterPoint;

	const CPolygon *polyArray = poly->GetPolygonR();

	if (polyArray == nullptr)
	{
		return;
	}

	Matrix objMx = poly->GetMg();
	BaseObject *obj = (BaseObject*)Get();
	BaseTag * motag = obj->GetTag(ID_MOTAGDATA);
	if (motag == nullptr)
		motag = obj->MakeTag(ID_MOTAGDATA);
	if (motag == nullptr)
	{
		return;
	}
	BaseTag * polyObjMocacheTag = obj->GetTag(ID_MOBAKETAG);

	GetMoDataMessage modataMsg = GetMoDataMessage();
	modataMsg.index = 0;

	if (polyObjMocacheTag)
	{
		polyObjMocacheTag->Message(MSG_GET_MODATA, &modataMsg);
	}
	BaseTag *objTag = obj->GetTag(ID_MOTAGDATA);
	if (objTag)
		objTag->Message(MSG_GET_MODATA, &modataMsg);

	if (!modataMsg.modata)
	{
		if (!motag->Message(MSG_GET_MODATA, &modataMsg) || !modataMsg.modata)
		{
			return;
		}
	}

	Float startingTimeA = GeGetMilliSeconds();
	MoData *md = modataMsg.modata;

	if (md)
	{

		maxon::BaseArray<Vector> centerPointArray;

		maxon::BaseArray< maxon::BaseArray<Vector> > polyPointArray;
		maxon::BaseArray<Vector> originalCenterArray;
		md->GetAutoLock();
		MDArray<Matrix> matrixArray;
		maxon::BaseArray<Matrix> originalMatrixArray;

		Int32 polygonCount = poly->GetPolygonCount();
		Int32 pointCount = poly->GetPointCount();
		const CPolygon* polygons = poly->GetPolygonR();
		const Vector *posArray = poly->GetPointR();

		
		// Get the center points of the various polygons
		for (Int32 polyIndex = 0; polyIndex < polygonCount; polyIndex++)
		{

			Vector centerPointOfPolygon = Vector(0);
			if (polygons[polyIndex].c == polygons[polyIndex].d)
			{
				centerPointOfPolygon += posArray[polygons[polyIndex].a];
				centerPointOfPolygon += posArray[polygons[polyIndex].b];
				centerPointOfPolygon += posArray[polygons[polyIndex].c];

				centerPointOfPolygon = (centerPointOfPolygon / 3.0);

			}
			else
			{
				centerPointOfPolygon += posArray[polygons[polyIndex].a];
				centerPointOfPolygon += posArray[polygons[polyIndex].b];
				centerPointOfPolygon += posArray[polygons[polyIndex].c];
				centerPointOfPolygon += posArray[polygons[polyIndex].d];

				centerPointOfPolygon = (centerPointOfPolygon / 4.0);
			}
			centerPointOfPolygon = Vector(polyIndex * 10, polyIndex * 10, polyIndex * 10);

			resultVector = originalCenterPoint.Append(centerPointOfPolygon);
			resultVector = centerPointArray.Append(centerPointOfPolygon);
		}

		md->SetCount(Int32(centerPointArray.GetCount()));
		matrixArray = md->GetMatrixArray(MODATA_MATRIX);

		MDArray<Vector> setMtxColorArray = md->GetVectorArray(MODATA_COLOR);
		MDArray<Vector> setMtxColorArra2y = md->GetVectorArray(MODATA_COLOR);

		dataInstance = op->GetDataInstance();
		const CustomDataType * effectorsraw = dataInstance->GetCustomDataType(idEffectorList, CUSTOMDATATYPE_INEXCLUDE_LIST);
		InExcludeData * effectors = nullptr;
		if (effectorsraw)
			effectors = (InExcludeData*)effectorsraw;

		resultVoid = originalMatrixArray.Resize(Int32(centerPointArray.GetCount()));
		// Generate the matrices
		for (Int32 centerIndex = 0; centerIndex < Int32(centerPointArray.GetCount()); centerIndex++)
		{
			Vector normal = CalcFaceNormal(posArray, polyArray[centerIndex]);

			Vector upVector = Vector(0, 1, 0);

			normal = normal;
			normal.Normalize();
			if (1.0 - abs(Dot(normal, upVector)) < .003)
			{
				upVector = Vector(0, 0, -1);

			}
			Vector z = normal;
			z.Normalize();
			Vector temp = Cross(upVector, z);

			Vector y = Cross(z, temp);
			y.Normalize();

			Vector x = Cross(y, z);
			x.Normalize();

			matrixArray[centerIndex] = Matrix(centerPointArray[centerIndex], x, y, z);
		}

		// Runs through the effectors
		if (effectors)
		{
			Effector_PassData emsg = Effector_PassData();
			emsg.op = op;
			emsg.md = md;
			emsg.weight = 1.0;
			emsg.thread = nullptr;
			Int32 flags = 0;
			for (Int i = 0; i < effectors->GetObjectCount(); i++)
			{
				BaseObject * e = (BaseObject*)effectors->ObjectFromIndex(doc, i);
				if (!e)
					continue;
				if (!e->GetDeformMode())
					continue;
				flags = effectors->GetFlags(doc, e);
				if (e != nullptr && e->GetDeformMode() == true && (flags & 1) == 1)
				{
					e->Message(MSG_EXECUTE_EFFECTOR, &emsg);
				}
			}
		}

		MDArray<Matrix> itemsMtxArray = md->GetMatrixArray(MODATA_MATRIX);
		Int32 matrixItemCnt = Int32(md->GetCount());
		ApplicationOutput("New run");
		for (Int32 centerIndex = 0; centerIndex < matrixItemCnt; centerIndex++)
			ApplicationOutput("Effector pos " + String::VectorToString(itemsMtxArray[centerIndex].off));

	}
	return;
}




Bool RegisterEffectorTest(Bool hideplugin)
{
	return RegisterObjectPlugin(ID_EFFECTORTEST, "Effector Move"_s, OBJECT_GENERATOR | OBJECT_ISSPLINE | OBJECT_INPUT, EffectorTestClass::Alloc, "OEffectorTest"_s, AutoBitmap("circle.tif"_s), 0);
}

I am confused what the difference between R23 and R25 could be that is responsible for the latest version not to work the same way or is it purely coincidental that my R23 version worked?

Any help would be greatly appreciated.

John Terenece

Hello @JohnTerenece,

thank you for reaching out to us. Unfortunately, our answer is mostly the same as it was for the previous topic on this plugin of yours.

  1. You are using the MoData tags in a way not intended by us. It is not supported by our SDK to implement a custom MoGraph generator and you are therefore not intended to populate and manage your own MoData tags. There is nothing preventing you from doing it anyways, this however will then be out of scope of support as declared in our Forum Guidelines under "Scope of Support".
  2. We cannot debug your code for you, especially when the employed techniques are a violation of point one listed here. This is also outlined in "Scope of Support".

You are also misusing the maxon API. Specifically, the error system, as you cannot just step over all errors which are returned. Which is what you do when you store them in some fields attached to your plugin implementation; never to be used or looked at again. Which can introduce all sorts of problems. The cases I saw do look unlikely to go wrong, but this is still a fundamental misuse of our API. See the end of my posting for details.

Finally, about your question. I would look at your CheckDirty(), since this is the likely culprit for your problems. But we cannot debug that for you. When you can show us that something is not being flagged as dirty what was flagged before, we will be glad to provide a context for that. Please also note that example code should be condensed as also outlined in our Forum Guidelines.

I understand that this is not the support you did hope for, but these limitations of scope of support must be enforced by us to a certain degree, as it would otherwise become a boundless task.

Thank you for your understanding,
Ferdinand

maxon::Result Example

What you do:

void EffectorTestClass::CheckDirty(BaseObject *op, BaseDocument *doc)
{
    // ...

    if (op->GetDown() != nullptr)
        // This could fail and you just step over it.
        resultBaseObject = objArray.Append(op->GetDown());

    // ...
    flags = data->GetFlags(doc, object);
    if ((flags & 1) == 1)
    {
        // This could fail and you just step over it.
        resultBaseObject = objArray.Append(object);
    }

    // ...
}

What you should do:

void EffectorTestClass::CheckDirty(BaseObject *op, BaseDocument *doc)
{
    // An error scope handler that brings the classic and maxon API together.
    iferr_scope_handler 
    {
        // you could also write to one of the loggers here, e.g.
        ApplicationOutput("Error in CheckDirty() @", err);
        return false;
    }
    // ...

    if (op->GetDown() != nullptr)
        objArray.Append(op->GetDown()) iferr_return;

    // ...
    flags = data->GetFlags(doc, object);
    if ((flags & 1) == 1)
    {
        objArray.Append(object) iferr_return;
    }

    // ...
}