Solved SendModelingCommand() in R20 and R21

Hello! I have a problem with SendModelingCommand().
I'm compiling my plugin for 2 versions of Cinema 4D and i noticed that code below works perfectly on R20 but returns false in R21-22.
Is there some changes in SendModelingCommand() use in R21?

void MyPlugin::Disconnect(BaseObject* main) {
	ModelingCommandData mcd;
	BaseContainer bc1;
	bc1.SetData(MDATA_DISCONNECT_PRESERVEGROUPS, false);
	mcd.doc = main->GetDocument();
	mcd.op = main;
	mcd.mode = MODELINGCOMMANDMODE::POLYGONSELECTION;
	mcd.flags = MODELINGCOMMANDFLAGS::CREATEUNDO;
	mcd.bc = &bc1;
	if (!SendModelingCommand(MCOMMAND_DISCONNECT, mcd)) ApplicationOutput("Disconnect error.");
}

@Danchyg1337 said in SendModelingCommand() in R20 and R21:

mcd.flags = MODELINGCOMMANDFLAGS::CREATEUNDO;

sorry I overlooked the issue, the main problem here is that you pass mcd.flags = MODELINGCOMMANDFLAGS::CREATEUNDO;

And creating an undo step in a threading environment (aka during the scene execution) is not allowed, so you should pass MODELINGCOMMANDFLAGS::NONE;

Now regarding your question, since I think he can still be valid one because of some modeling operator explicitly operate on the current object but also need a document (like CurrentStateToObject) here a very naive approach that may not work in all condition (e.g. no aliastrans, so link may be lost)

	AutoAlloc<BaseDocument> basedocBuffer;
	BaseObject* clonedOp = (BaseObject*)op->GetClone(COPYFLAGS::NONE, nullptr);
	basedocBuffer->InsertObject(clonedOp, nullptr, nullptr);
	basedocBuffer->ExecutePasses(thread, true, true, true, BUILDFLAGS::INTERNALRENDERER);

	ModelingCommandData mcd;
	BaseContainer bc;

	bc.SetData(MDATA_DISCONNECT_PRESERVEGROUPS, false);

	mcd.doc = basedocBuffer;
	mcd.op = clonedOp;
	mcd.mode = MODELINGCOMMANDMODE::POLYGONSELECTION;
	mcd.flags = MODELINGCOMMANDFLAGS::NONE;
	mcd.bc = &bc;

	if (!SendModelingCommand(MCOMMAND_DISCONNECT, mcd))
		ApplicationOutput("Disconnect error.");

	clonedOp->CopyTo(op, COPYFLAGS::NONE, nullptr);
	clonedOp->CopyMatrixTo(op);
	clonedOp->CopyTagsTo(op, NOTOK, NOTOK, NOTOK, nullptr);

And why it was worked previously? I would say it didn't properly, but the CriticalStop wasn't there internally, so we simply returned without knowing there is an issue.

Hope it helps,
Cheers,
Maxime.

Hi @Danchyg1337 here I have no issue it's working as it was previously, can you share a scene or maybe the context of your call of SendModelingCommand?

Cheers,
Maxime.

@m_adam I made my code as much clear as i could and i still getting false from SendModelingCommand().

Bool MyPlugin::ModifyObject(BaseObject* mod, BaseDocument* doc, BaseObject* op, const Matrix& op_mg, const Matrix& mod_mg, Float lod, Int32 flags, BaseThread* thread)
{
	Disconnect(op);
	return true;
}

While i was trying to compile the first version in R21 there was a lot of exceptions in console, i followed them and found HAS_EXCEPTIONS 0 line in apibase.h. I changed it to 1 and project did compile. Maybe it is something with that because everything else with code is ok.

I forgot to download R21 project tool and used R20 instead, that is why i got to change HAS_EXCEPTIONS.
Although i recompiled it without HAS_EXCEPTIONS 1, it still doesn't work.

@Danchyg1337 said in SendModelingCommand() in R20 and R21:

SendModelingCommand

As stated in the documentation it's not a good habit to do a SendModelingCommand in the ModifyObject see SendModelingCommand
So the best workaround would be to create a temporary document, insert op into this tempo doc, ExecutePass to build the cache, operate your modeling operation on this object, and return a clone in your Deformer.

Cheers,
Maxime.

@m_adam Thanks for your advice. I'm using multiple Disconnect calls in ModifyObject().
I'm trying to do it, like you said, but obviously i'm doing it wrong. Can you give me another one advice on what i am doing wrong?

AutoAlloc<BaseDocument> basedocBuffer;
	basedocBuffer->InsertObject((BaseObject*)op->GetClone(COPYFLAGS::CACHE_BUILD, nullptr), nullptr, nullptr);
	basedocBuffer->ExecutePasses(thread, true, true, true, BUILDFLAGS::NONE);
	Disconnect(basedocBuffer->GetFirstObject());
	op = basedocBuffer->GetFirstObject();

That part of code still in ModifyObject(). I don't know how to avoid using it in ModifyObject().

@Danchyg1337 said in SendModelingCommand() in R20 and R21:

mcd.flags = MODELINGCOMMANDFLAGS::CREATEUNDO;

sorry I overlooked the issue, the main problem here is that you pass mcd.flags = MODELINGCOMMANDFLAGS::CREATEUNDO;

And creating an undo step in a threading environment (aka during the scene execution) is not allowed, so you should pass MODELINGCOMMANDFLAGS::NONE;

Now regarding your question, since I think he can still be valid one because of some modeling operator explicitly operate on the current object but also need a document (like CurrentStateToObject) here a very naive approach that may not work in all condition (e.g. no aliastrans, so link may be lost)

	AutoAlloc<BaseDocument> basedocBuffer;
	BaseObject* clonedOp = (BaseObject*)op->GetClone(COPYFLAGS::NONE, nullptr);
	basedocBuffer->InsertObject(clonedOp, nullptr, nullptr);
	basedocBuffer->ExecutePasses(thread, true, true, true, BUILDFLAGS::INTERNALRENDERER);

	ModelingCommandData mcd;
	BaseContainer bc;

	bc.SetData(MDATA_DISCONNECT_PRESERVEGROUPS, false);

	mcd.doc = basedocBuffer;
	mcd.op = clonedOp;
	mcd.mode = MODELINGCOMMANDMODE::POLYGONSELECTION;
	mcd.flags = MODELINGCOMMANDFLAGS::NONE;
	mcd.bc = &bc;

	if (!SendModelingCommand(MCOMMAND_DISCONNECT, mcd))
		ApplicationOutput("Disconnect error.");

	clonedOp->CopyTo(op, COPYFLAGS::NONE, nullptr);
	clonedOp->CopyMatrixTo(op);
	clonedOp->CopyTagsTo(op, NOTOK, NOTOK, NOTOK, nullptr);

And why it was worked previously? I would say it didn't properly, but the CriticalStop wasn't there internally, so we simply returned without knowing there is an issue.

Hope it helps,
Cheers,
Maxime.

@m_adam MODELINGCOMMANDFLAGS::NONE works! Thanks again for helping me out!