Solved Deformer update and Fields

hello,

i'm creating a deformer and i want to add the new falloff / fields option.
everything seem to work fine but the deformer doesn't update when i move / modify a linear field or any field i've added.
(the function modifyObject() isn't called)

If i change frame or force the update with the a key, it does work.

using the CheckDirty function seem to be the solution.

void Spherify::CheckDirty(BaseObject* op, BaseDocument* doc)
{
	if (falloff)
	{
		BaseContainer *data = op->GetDataInstance();
		Int32 dirty = falloff->GetDirty(doc, data);
		if (dirty == lastFalloffDirtyCheck) 
                      return;
		op->SetDirty(DIRTYFLAGS::DATA);
		lastFalloffDirtyCheck = dirty;
	}
		
}

the full code for the spherify deformer :

// deformer object example

#include "c4d.h"
#include "c4d_symbols.h"
#include "main.h"
#include "ospherifydeformer.h"
#include "c4d_falloffdata.h"


#define HANDLE_CNT 2

class Spherify : public ObjectData
{
public:
	virtual Bool Init(GeListNode* node);

	virtual Bool Message			(GeListNode* node, Int32 type, void* data);
	virtual void GetDimension		(BaseObject* op, Vector* mp, Vector* rad);
	virtual DRAWRESULT Draw			(BaseObject* op, DRAWPASS type, BaseDraw* bd, BaseDrawHelp* bh);
	virtual void GetHandle			(BaseObject* op, Int32 i, HandleInfo& info);
	virtual Int32 DetectHandle		(BaseObject* op, BaseDraw* bd, Int32 x, Int32 y, QUALIFIER qualifier);
	virtual Bool MoveHandle			(BaseObject* op, BaseObject* undo, const Vector& mouse_pos, Int32 hit_id, QUALIFIER qualifier, BaseDraw* bd);
	virtual Bool ModifyObject		(BaseObject* op, BaseDocument* doc, BaseObject* mod, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread);



	
	AutoAlloc<C4D_Falloff>		falloff;
	Int32						lastFalloffDirtyCheck;
	
	virtual Int32				GetHandleCount(BaseObject *op);
	
	virtual void				SetHandle(BaseObject *op, Int32 i, Vector p, const HandleInfo &info);
	virtual Bool				CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn);
	virtual Bool				GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags);
	virtual Bool            AddToExecution(BaseObject *op, PriorityList *list);
	virtual EXECUTIONRESULT Execute(BaseObject *op, BaseDocument *doc, BaseThread *bt, Int32 priority, EXECUTIONFLAGS flags);
	virtual void				CheckDirty(BaseObject* op, BaseDocument* doc);


	static NodeData* Alloc() { return NewObjClear(Spherify); }
};



Int32 Spherify::GetHandleCount(BaseObject *op)
{
	BaseContainer *bc = op->GetDataInstance();
	if (!bc) 
		return 0;
	if (falloff) 
		return falloff->GetHandleCount(bc) + HANDLE_CNT;
	return 0;
}

void Spherify::SetHandle(BaseObject *op, Int32 i, Vector p, const HandleInfo &info)
{
	BaseContainer *bc = op->GetDataInstance();
	if (!bc) 
		return;
	if (falloff) 
		falloff->SetHandle(i, p, bc, info);
}

Bool Spherify::CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn)
{
	Spherify *df = (Spherify*)dest;
	if (!df) 
		return false;
	if (falloff && df->falloff)
		if (!falloff->CopyTo(df->falloff)) 
			return false;
	return ObjectData::CopyTo(dest, snode, dnode, flags, trn);
}

Bool Spherify::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
{
	BaseObject *op = (BaseObject*)node;
	if (!op) 
		return false;
	BaseContainer *data = op->GetDataInstance();
	if (!data) 
		return false;

	if (!description->LoadDescription(op->GetType())) 
		return false;

	//---------------------------------
	// Add the falloff interface
	if (falloff)
	{
		if (!falloff->SetMode(FIELDS, data)) // The falloff parameters have to have been setup before it can be added to the description, this like makes sure of that
			return false; 
		if (!falloff->AddFalloffToDescription(description, data, DESCFLAGS_DESC::NONE)) 
			return false;
	}

	flags |= DESCFLAGS_DESC::LOADED;

	return true;
}


void Spherify::CheckDirty(BaseObject* op, BaseDocument* doc)
{
	if (falloff)
	{
		BaseContainer *data = op->GetDataInstance();
		Int32 dirty = falloff->GetDirty(doc, data);
		if (dirty == lastFalloffDirtyCheck) 
                        return;
		op->SetDirty(DIRTYFLAGS::DATA);
		lastFalloffDirtyCheck = dirty;
	}
		
}

Bool Spherify::AddToExecution(BaseObject *op, PriorityList *list) {
	list->Add(op, EXECUTIONPRIORITY_INITIAL, EXECUTIONFLAGS::NONE);
	return TRUE;

}
EXECUTIONRESULT Spherify::Execute(BaseObject *op, BaseDocument *doc, BaseThread *bt, Int32 priority, EXECUTIONFLAGS flags)
{
	BaseContainer *data = op->GetDataInstance();
	if (!data)
		return EXECUTIONRESULT::USERBREAK;

	if (falloff)
		if (!falloff->InitFalloff(data, doc, op)) 
			return EXECUTIONRESULT::OUTOFMEMORY;
	return EXECUTIONRESULT::OK;
}



Bool Spherify::Message(GeListNode* node, Int32 type, void* data)
{
	if (type == MSG_MENUPREPARE)
	{
		((BaseObject*)node)->SetDeformMode(true);
	}
	return true;
}

Bool Spherify::ModifyObject(BaseObject* mod, BaseDocument* doc, BaseObject* op, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread)
{


	BaseContainer* data = mod->GetDataInstance();

	Vector	 p, *padr = nullptr;
	Matrix	 m, im;
	Int32		 i, pcnt;
	Float		 rad = data->GetFloat(SPHERIFYDEFORMER_RADIUS), strength = data->GetFloat(SPHERIFYDEFORMER_STRENGTH);
	Float		 s;
	Float32* weight = nullptr;

	if (!op->IsInstanceOf(Opoint))
		return true;

	padr = ToPoint(op)->GetPointW();
	pcnt = ToPoint(op)->GetPointCount();
	if (!pcnt)
		return true;


	FieldInput inputs(padr, pcnt, op_mg);
	Bool outputsOK = falloff->PreSample(doc, mod, inputs, FIELDSAMPLE_FLAG::VALUE);
	Float fallOffSampleValue(1.0);


	weight = ToPoint(op)->CalcVertexMap(mod);

	m	 = (~mod_mg) * op_mg;	// op  ->  world  ->  modifier
	im = ~m;



	for (i = 0; i < pcnt; i++)
	{
		if (thread && !(i & 63) && thread->TestBreak())
			break;
		p = m * padr[i];
		s = strength;
		if (weight)
			s *= weight[i];
		if (outputsOK)
			falloff->Sample(p, &fallOffSampleValue, true, 0.0, nullptr, i);
		s *= fallOffSampleValue;
		p = s * (!p * rad) + (1.0 - s) * p;
		padr[i] = im * p;
	}

	DeleteMem(weight);
	op->Message(MSG_UPDATE);

	return true;
}

void Spherify::GetDimension(BaseObject* op, Vector* mp, Vector* rad)
{
	BaseContainer* data = op->GetDataInstance();
	*mp	 = Vector(0.0);
	*rad = Vector(data->GetFloat(SPHERIFYDEFORMER_RADIUS));
}

DRAWRESULT Spherify::Draw(BaseObject* op, DRAWPASS drawpass, BaseDraw* bd, BaseDrawHelp* bh)
{

	if (!op->GetDeformMode()) 
		return DRAWRESULT::SKIP;
	BaseContainer *bc = op->GetDataInstance();
	if (!bc) 
		return DRAWRESULT::FAILURE;
	if (falloff) 
		falloff->Draw(bd, bh, drawpass, bc);
	

	if (drawpass == DRAWPASS::OBJECT)
	{
		BaseContainer* data = op->GetDataInstance();
		Float	 rad = data->GetFloat(SPHERIFYDEFORMER_RADIUS);
		Matrix m = bh->GetMg();

		m.sqmat *= rad;

		bd->SetMatrix_Matrix(nullptr, Matrix());
		bd->SetPen(bd->GetObjectColor(bh, op));
		bd->DrawCircle(m);
		maxon::Swap(m.sqmat.v2, m.sqmat.v3);
		bd->DrawCircle(m);
		maxon::Swap(m.sqmat.v1, m.sqmat.v3);
		bd->DrawCircle(m);
	}
	else if (drawpass == DRAWPASS::HANDLES)
	{
		Int32			 i;
		Int32			 hitid = op->GetHighlightHandle(bd);
		HandleInfo info;

		bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
		bd->SetMatrix_Matrix(op, bh->GetMg());
		for (i = 0; i < HANDLE_CNT; i++)
		{
			GetHandle(op, i, info);
			if (hitid == i)
				bd->SetPen(GetViewColor(VIEWCOLOR_SELECTION_PREVIEW));
			else
				bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
			bd->DrawHandle(info.position, DRAWHANDLE::BIG, 0);
		}

		GetHandle(op, 1, info);
		bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
		bd->DrawLine(info.position, Vector(0.0), 0);
	}
	return DRAWRESULT::OK;
}

void Spherify::GetHandle(BaseObject* op, Int32 i, HandleInfo& info)
{
	BaseContainer* data = op->GetDataInstance();
	if (!data)
		return;
	
	
	if (falloff)  
		falloff->GetHandle(i, data, info);

	switch (i)
	{
		case 0:
			info.position.x	 = data->GetFloat(SPHERIFYDEFORMER_RADIUS);
			info.direction.x = 1.0;
			info.type = HANDLECONSTRAINTTYPE::LINEAR;
			break;

		case 1:
			info.position.x	 = data->GetFloat(SPHERIFYDEFORMER_STRENGTH) * 1000.0;
			info.direction.x = 1.0;
			info.type = HANDLECONSTRAINTTYPE::LINEAR;
			break;

		default: 
			break;
	}
}

Int32 Spherify::DetectHandle(BaseObject* op, BaseDraw* bd, Int32 x, Int32 y, QUALIFIER qualifier)
{
	if (qualifier & QUALIFIER::CTRL)
		return NOTOK;

	HandleInfo info;
	Matrix		 mg = op->GetMg();
	Int32			 i, ret = NOTOK;
	Vector		 p;

	for (i = 0; i < HANDLE_CNT; i++)
	{
		GetHandle(op, i, info);
		if (bd->PointInRange(mg * info.position, x, y))
		{
			ret = i;
			if (!(qualifier & QUALIFIER::SHIFT))
				break;
		}
	}
	return ret;
}

Bool Spherify::MoveHandle(BaseObject* op, BaseObject* undo, const Vector& mouse_pos, Int32 hit_id, QUALIFIER qualifier, BaseDraw* bd)
{
	BaseContainer* dst = op->GetDataInstance();

	HandleInfo info;

	Float val = mouse_pos.x;
	GetHandle(op, hit_id, info);

	if (bd)
	{
		Matrix mg	 = op->GetUpMg() * undo->GetMl();
		Vector pos = bd->ProjectPointOnLine(mg * info.position, mg.sqmat * info.direction, mouse_pos.x, mouse_pos.y);
		val = Dot(~mg * pos, info.direction);
	}

	switch (hit_id)
	{
		case 0:
			dst->SetFloat(SPHERIFYDEFORMER_RADIUS, ClampValue(val, 0.0_f, (Float) MAXRANGE));
			break;

		case 1:
			dst->SetFloat(SPHERIFYDEFORMER_STRENGTH, Clamp01(val * 0.001));
			break;

		default:
			break;
	}
	return true;
}

Bool Spherify::Init(GeListNode* node)
{
	BaseObject*		 op = (BaseObject*)node;
	BaseContainer* data = op->GetDataInstance();

	data->SetFloat(SPHERIFYDEFORMER_RADIUS, 200.0);
	data->SetFloat(SPHERIFYDEFORMER_STRENGTH, 0.5);
	
	if (falloff)
	{
		if (!falloff->InitFalloff(nullptr, nullptr, op))
			return false;
		lastFalloffDirtyCheck = -1;
	}


	return true;
}

// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_SPHERIFYOBJECT 1001158

Bool RegisterSpherify()
{
	return RegisterObjectPlugin(ID_SPHERIFYOBJECT, GeLoadString(IDS_SPHERIZE), OBJECT_MODIFIER | OBJECT_CALL_ADDEXECUTION, Spherify::Alloc, "Ospherifydeformer"_s, AutoBitmap("spherify.tif"_s), 0);
}

using the CheckDirty function seem to be the solution.

void Spherify::CheckDirty(BaseObject* op, BaseDocument* doc)
{
	if (falloff)
	{
		BaseContainer *data = op->GetDataInstance();
		Int32 dirty = falloff->GetDirty(doc, data);
		if (dirty == lastFalloffDirtyCheck) 
                      return;
		op->SetDirty(DIRTYFLAGS::DATA);
		lastFalloffDirtyCheck = dirty;
	}
		
}

the full code for the spherify deformer :

// deformer object example

#include "c4d.h"
#include "c4d_symbols.h"
#include "main.h"
#include "ospherifydeformer.h"
#include "c4d_falloffdata.h"


#define HANDLE_CNT 2

class Spherify : public ObjectData
{
public:
	virtual Bool Init(GeListNode* node);

	virtual Bool Message			(GeListNode* node, Int32 type, void* data);
	virtual void GetDimension		(BaseObject* op, Vector* mp, Vector* rad);
	virtual DRAWRESULT Draw			(BaseObject* op, DRAWPASS type, BaseDraw* bd, BaseDrawHelp* bh);
	virtual void GetHandle			(BaseObject* op, Int32 i, HandleInfo& info);
	virtual Int32 DetectHandle		(BaseObject* op, BaseDraw* bd, Int32 x, Int32 y, QUALIFIER qualifier);
	virtual Bool MoveHandle			(BaseObject* op, BaseObject* undo, const Vector& mouse_pos, Int32 hit_id, QUALIFIER qualifier, BaseDraw* bd);
	virtual Bool ModifyObject		(BaseObject* op, BaseDocument* doc, BaseObject* mod, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread);



	
	AutoAlloc<C4D_Falloff>		falloff;
	Int32						lastFalloffDirtyCheck;
	
	virtual Int32				GetHandleCount(BaseObject *op);
	
	virtual void				SetHandle(BaseObject *op, Int32 i, Vector p, const HandleInfo &info);
	virtual Bool				CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn);
	virtual Bool				GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags);
	virtual Bool            AddToExecution(BaseObject *op, PriorityList *list);
	virtual EXECUTIONRESULT Execute(BaseObject *op, BaseDocument *doc, BaseThread *bt, Int32 priority, EXECUTIONFLAGS flags);
	virtual void				CheckDirty(BaseObject* op, BaseDocument* doc);


	static NodeData* Alloc() { return NewObjClear(Spherify); }
};



Int32 Spherify::GetHandleCount(BaseObject *op)
{
	BaseContainer *bc = op->GetDataInstance();
	if (!bc) 
		return 0;
	if (falloff) 
		return falloff->GetHandleCount(bc) + HANDLE_CNT;
	return 0;
}

void Spherify::SetHandle(BaseObject *op, Int32 i, Vector p, const HandleInfo &info)
{
	BaseContainer *bc = op->GetDataInstance();
	if (!bc) 
		return;
	if (falloff) 
		falloff->SetHandle(i, p, bc, info);
}

Bool Spherify::CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn)
{
	Spherify *df = (Spherify*)dest;
	if (!df) 
		return false;
	if (falloff && df->falloff)
		if (!falloff->CopyTo(df->falloff)) 
			return false;
	return ObjectData::CopyTo(dest, snode, dnode, flags, trn);
}

Bool Spherify::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
{
	BaseObject *op = (BaseObject*)node;
	if (!op) 
		return false;
	BaseContainer *data = op->GetDataInstance();
	if (!data) 
		return false;

	if (!description->LoadDescription(op->GetType())) 
		return false;

	//---------------------------------
	// Add the falloff interface
	if (falloff)
	{
		if (!falloff->SetMode(FIELDS, data)) // The falloff parameters have to have been setup before it can be added to the description, this like makes sure of that
			return false; 
		if (!falloff->AddFalloffToDescription(description, data, DESCFLAGS_DESC::NONE)) 
			return false;
	}

	flags |= DESCFLAGS_DESC::LOADED;

	return true;
}


void Spherify::CheckDirty(BaseObject* op, BaseDocument* doc)
{
	if (falloff)
	{
		BaseContainer *data = op->GetDataInstance();
		Int32 dirty = falloff->GetDirty(doc, data);
		if (dirty == lastFalloffDirtyCheck) 
                        return;
		op->SetDirty(DIRTYFLAGS::DATA);
		lastFalloffDirtyCheck = dirty;
	}
		
}

Bool Spherify::AddToExecution(BaseObject *op, PriorityList *list) {
	list->Add(op, EXECUTIONPRIORITY_INITIAL, EXECUTIONFLAGS::NONE);
	return TRUE;

}
EXECUTIONRESULT Spherify::Execute(BaseObject *op, BaseDocument *doc, BaseThread *bt, Int32 priority, EXECUTIONFLAGS flags)
{
	BaseContainer *data = op->GetDataInstance();
	if (!data)
		return EXECUTIONRESULT::USERBREAK;

	if (falloff)
		if (!falloff->InitFalloff(data, doc, op)) 
			return EXECUTIONRESULT::OUTOFMEMORY;
	return EXECUTIONRESULT::OK;
}



Bool Spherify::Message(GeListNode* node, Int32 type, void* data)
{
	if (type == MSG_MENUPREPARE)
	{
		((BaseObject*)node)->SetDeformMode(true);
	}
	return true;
}

Bool Spherify::ModifyObject(BaseObject* mod, BaseDocument* doc, BaseObject* op, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread)
{


	BaseContainer* data = mod->GetDataInstance();

	Vector	 p, *padr = nullptr;
	Matrix	 m, im;
	Int32		 i, pcnt;
	Float		 rad = data->GetFloat(SPHERIFYDEFORMER_RADIUS), strength = data->GetFloat(SPHERIFYDEFORMER_STRENGTH);
	Float		 s;
	Float32* weight = nullptr;

	if (!op->IsInstanceOf(Opoint))
		return true;

	padr = ToPoint(op)->GetPointW();
	pcnt = ToPoint(op)->GetPointCount();
	if (!pcnt)
		return true;


	FieldInput inputs(padr, pcnt, op_mg);
	Bool outputsOK = falloff->PreSample(doc, mod, inputs, FIELDSAMPLE_FLAG::VALUE);
	Float fallOffSampleValue(1.0);


	weight = ToPoint(op)->CalcVertexMap(mod);

	m	 = (~mod_mg) * op_mg;	// op  ->  world  ->  modifier
	im = ~m;



	for (i = 0; i < pcnt; i++)
	{
		if (thread && !(i & 63) && thread->TestBreak())
			break;
		p = m * padr[i];
		s = strength;
		if (weight)
			s *= weight[i];
		if (outputsOK)
			falloff->Sample(p, &fallOffSampleValue, true, 0.0, nullptr, i);
		s *= fallOffSampleValue;
		p = s * (!p * rad) + (1.0 - s) * p;
		padr[i] = im * p;
	}

	DeleteMem(weight);
	op->Message(MSG_UPDATE);

	return true;
}

void Spherify::GetDimension(BaseObject* op, Vector* mp, Vector* rad)
{
	BaseContainer* data = op->GetDataInstance();
	*mp	 = Vector(0.0);
	*rad = Vector(data->GetFloat(SPHERIFYDEFORMER_RADIUS));
}

DRAWRESULT Spherify::Draw(BaseObject* op, DRAWPASS drawpass, BaseDraw* bd, BaseDrawHelp* bh)
{

	if (!op->GetDeformMode()) 
		return DRAWRESULT::SKIP;
	BaseContainer *bc = op->GetDataInstance();
	if (!bc) 
		return DRAWRESULT::FAILURE;
	if (falloff) 
		falloff->Draw(bd, bh, drawpass, bc);
	

	if (drawpass == DRAWPASS::OBJECT)
	{
		BaseContainer* data = op->GetDataInstance();
		Float	 rad = data->GetFloat(SPHERIFYDEFORMER_RADIUS);
		Matrix m = bh->GetMg();

		m.sqmat *= rad;

		bd->SetMatrix_Matrix(nullptr, Matrix());
		bd->SetPen(bd->GetObjectColor(bh, op));
		bd->DrawCircle(m);
		maxon::Swap(m.sqmat.v2, m.sqmat.v3);
		bd->DrawCircle(m);
		maxon::Swap(m.sqmat.v1, m.sqmat.v3);
		bd->DrawCircle(m);
	}
	else if (drawpass == DRAWPASS::HANDLES)
	{
		Int32			 i;
		Int32			 hitid = op->GetHighlightHandle(bd);
		HandleInfo info;

		bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
		bd->SetMatrix_Matrix(op, bh->GetMg());
		for (i = 0; i < HANDLE_CNT; i++)
		{
			GetHandle(op, i, info);
			if (hitid == i)
				bd->SetPen(GetViewColor(VIEWCOLOR_SELECTION_PREVIEW));
			else
				bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
			bd->DrawHandle(info.position, DRAWHANDLE::BIG, 0);
		}

		GetHandle(op, 1, info);
		bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
		bd->DrawLine(info.position, Vector(0.0), 0);
	}
	return DRAWRESULT::OK;
}

void Spherify::GetHandle(BaseObject* op, Int32 i, HandleInfo& info)
{
	BaseContainer* data = op->GetDataInstance();
	if (!data)
		return;
	
	
	if (falloff)  
		falloff->GetHandle(i, data, info);

	switch (i)
	{
		case 0:
			info.position.x	 = data->GetFloat(SPHERIFYDEFORMER_RADIUS);
			info.direction.x = 1.0;
			info.type = HANDLECONSTRAINTTYPE::LINEAR;
			break;

		case 1:
			info.position.x	 = data->GetFloat(SPHERIFYDEFORMER_STRENGTH) * 1000.0;
			info.direction.x = 1.0;
			info.type = HANDLECONSTRAINTTYPE::LINEAR;
			break;

		default: 
			break;
	}
}

Int32 Spherify::DetectHandle(BaseObject* op, BaseDraw* bd, Int32 x, Int32 y, QUALIFIER qualifier)
{
	if (qualifier & QUALIFIER::CTRL)
		return NOTOK;

	HandleInfo info;
	Matrix		 mg = op->GetMg();
	Int32			 i, ret = NOTOK;
	Vector		 p;

	for (i = 0; i < HANDLE_CNT; i++)
	{
		GetHandle(op, i, info);
		if (bd->PointInRange(mg * info.position, x, y))
		{
			ret = i;
			if (!(qualifier & QUALIFIER::SHIFT))
				break;
		}
	}
	return ret;
}

Bool Spherify::MoveHandle(BaseObject* op, BaseObject* undo, const Vector& mouse_pos, Int32 hit_id, QUALIFIER qualifier, BaseDraw* bd)
{
	BaseContainer* dst = op->GetDataInstance();

	HandleInfo info;

	Float val = mouse_pos.x;
	GetHandle(op, hit_id, info);

	if (bd)
	{
		Matrix mg	 = op->GetUpMg() * undo->GetMl();
		Vector pos = bd->ProjectPointOnLine(mg * info.position, mg.sqmat * info.direction, mouse_pos.x, mouse_pos.y);
		val = Dot(~mg * pos, info.direction);
	}

	switch (hit_id)
	{
		case 0:
			dst->SetFloat(SPHERIFYDEFORMER_RADIUS, ClampValue(val, 0.0_f, (Float) MAXRANGE));
			break;

		case 1:
			dst->SetFloat(SPHERIFYDEFORMER_STRENGTH, Clamp01(val * 0.001));
			break;

		default:
			break;
	}
	return true;
}

Bool Spherify::Init(GeListNode* node)
{
	BaseObject*		 op = (BaseObject*)node;
	BaseContainer* data = op->GetDataInstance();

	data->SetFloat(SPHERIFYDEFORMER_RADIUS, 200.0);
	data->SetFloat(SPHERIFYDEFORMER_STRENGTH, 0.5);
	
	if (falloff)
	{
		if (!falloff->InitFalloff(nullptr, nullptr, op))
			return false;
		lastFalloffDirtyCheck = -1;
	}


	return true;
}

// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_SPHERIFYOBJECT 1001158

Bool RegisterSpherify()
{
	return RegisterObjectPlugin(ID_SPHERIFYOBJECT, GeLoadString(IDS_SPHERIZE), OBJECT_MODIFIER | OBJECT_CALL_ADDEXECUTION, Spherify::Alloc, "Ospherifydeformer"_s, AutoBitmap("spherify.tif"_s), 0);
}