Thanks for the response, it's all working properly now.
John Terenece
Thanks for the response, it's all working properly now.
John Terenece
Thanks for the detailed response.
I was able to successfully compile and run the code you provided but I'm running into some weirdness when trying to drag different parameters onto the tag.
Thanks for the response.
I tried implementing the small example bit of code you gave into my own and I was unable to get it properly working. Heres the project file I'm working from that's just a trimmed down version of the sdk example (cinema4dsdk.zip).
Inside of the example.assets under modules I was able to successfully get it to compile but when I tried to do it inside of the cinema4dsdk module it would continuously give errors that I was missing various header files. When I followed the usual procedure to include the various header files requried I started getting errors inside of the header files themselves saying they were missing other files that they had included that I can't find on my computer.
This is the first time I'm encountering these kinds of errors so I'm not entirely sure if I'm missing something extremely simple.
And just to be clear with what I'm attempting to do is I want to be able to catch if a parameter dragged onto my tag is coming from the object that the tag is attached to. With an Ocube my code properly works, with a Neutron Cube my code will not tell me that the dragged parameter's object matches the tag's object.
John Terenece
Hi.
When working on a tag plugin I've recently been encountering a problem when I try to interact with Capsules, my plugin is meant to have parameters dropped onto it from the object it is attached to.
In my code I can properly receive the drag and drop operation and get the information from the parameter that was dropped onto it, the issue comes in when I try to determine which object the parameter came from.
When it comes to the Capsules in the object manager my code does not return the results that I am expecting when I try to verify that the parameters were dragged from the object that the tag is on.
The code below is a stripped down version of my drag and drop code.
Bool TagExample::Message(GeListNode* node, Int32 type, void* t_data)
{
BaseDocument* doc = node->GetDocument();
if (!doc)
return TRUE;
BaseTag* tag = static_cast<BaseTag*> (node);
if (!tag)
return TRUE;
if (type == MSG_DRAGANDDROP)
{
DragAndDrop* dnd = static_cast<DragAndDrop*>(t_data);
if (!dnd)
return TRUE;
if (dnd->type == DRAGTYPE_DESCID)
{
DescPropertyDragData* dndid = static_cast<DescPropertyDragData*>(dnd->data);
AutoAlloc<AtomArray> dndidarr;
dndid->arr->CopyTo(dndidarr);
if (dndidarr->GetCount() <= 0)
return TRUE;
BaseObject* tagObject = tag->GetObject();
if (!tagObject)
return TRUE;
BaseObject *draggedObject = (BaseObject*)dndidarr->GetIndex(0);
if (!draggedObject)
return TRUE;
if (draggedObject != tagObject)
ApplicationOutput("Do not equal");
else
ApplicationOutput("The objects equal")
return TRUE;
}
}
return true;
}
For some reason when I drag a parameter from a Neutron Cube, that my tag is on, onto my tag I don't get the expected result of the dragged object and the tag object equalling each other. If I do the same procedure on a regular Cube (Ocube) I am able to properly catch that the objects are one and the same. The same behavior is encountered with other Capsules.
I'm not very familiar with Capsules, is there a specific way that Capsules need to be interacted with in order to achieve the desired results?
Any help would be greatly appreciated.
John Terenece
Thanks for the response, that's what I was afraid of.
John Terenece
Hi,
I'm working on a project that is made up of multiple tag plugins. What I'm trying to do is have the tags all separate in the list of Tags instead of under one folder.
Below I have a screenshot of the compiled base SDK for visual purposes.
So what I'm looking for is to have the three plugins inside of the "Cinema4dsdk Tags" dropdown not be inside of a submenu.
I know you can do this by compiling them all as separate projects but I would prefer to avoid going that route if there is an alternative.
Any help would be appreciated.
John Terenece
Thanks for the response.
I figured that something like this would be the case, will have to look into the options you mentioned.
John Terenece
Thanks for the detailed response.
I implemented the GeIsMainThread() into my code and that itself did not change any of the results I am getting which I would expect it to.
Running my code with a StartUndo and EndUndo results in this behavior StartEnd Undo.webm
I have tried the code without any AddUndo and this was the result No AddUndo.webm.
Sorry I wasn't clear in what I wanted the undos to do. As seen in both videos dropping one tag onto the other causes the dropped tag to move and the link field to change, what I am trying to do is have both of those being undone in a single undo so that the link field would be cleared and the dropped tag moved back to its original position.
I know that dragging a tag into the link field itself doesn't require an undo, that part of my code works perfectly fine.
John Terenece
Hi.
I am working on a tag plugin that will have a user drag & dropping another tag onto it, the dragged tag will then be set into a link field for use later. I successfully have the drag and drop working, my issue comes in when I try to implement undo functionality for the drag and drop.
With the code below (from the sdk) I have the tag only accepting tags of the same type as the plugin itself. After the drag and drop it takes two separate undos to successfully revert the link field to its previous state. I've tried the code below with the StartUndo/EndUndo and it always takes the two undos.
#include "c4d.h"
#include "c4d_symbols.h"
#include "lib_hair.h"
#include "main.h"
#include <customgui_descproperty.h>
#include "thairsdkrendering.h"
#define TESTPLUGIN_ID 9000
//////////////////////////////////////////////////////////////////////////
class HairRenderingTag : public TagData
{
INSTANCEOF(HairRenderingTag, TagData)
public:
virtual Bool Init(GeListNode* node);
virtual void Free(GeListNode* node);
virtual Bool Message(GeListNode* node, Int32 type, void* data);
virtual Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags);
static NodeData* Alloc() { return NewObjClear(HairRenderingTag); }
};
//////////////////////////////////////////////////////////////////////////
Bool HairRenderingTag::Init(GeListNode* node)
{
BaseContainer* bc = ((BaseList2D*)node)->GetDataInstance();
return true;
}
void HairRenderingTag::Free(GeListNode* node)
{
}
Bool HairRenderingTag::GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags)
{
if (!description->LoadDescription(node->GetType()))
{
}
const DescID* singleid = description->GetSingleDescID();
BaseContainer *dataInstance = ((BaseList2D*)node)->GetDataInstance(); // Get the container for the tag
DescID cid = DescLevel(1000, DTYPE_GROUP, 0);
if (!singleid || cid.IsPartOf(*singleid, nullptr)) // important to check for speedup c4d!
{
BaseContainer maingroup = GetCustomDataTypeDefault(DTYPE_GROUP);
maingroup.SetString(DESC_NAME, "Links"_s);
maingroup.SetInt32(DESC_DEFAULT, 1);
if (!description->SetParameter(cid, maingroup, DescLevel(TESTPLUGIN_ID)))
return true;
}
cid = DescLevel(1001, DTYPE_BASELISTLINK, 0);
if (!singleid || cid.IsPartOf(*singleid, NULL)) // important to check for speedup c4d!
{
BaseContainer bc;
bc = GetCustomDataTypeDefault(DTYPE_BASELISTLINK);
BaseContainer acceptedObjects;
acceptedObjects.InsData(TESTPLUGIN_ID, String()); // matrix
bc.SetContainer(DESC_ACCEPT, acceptedObjects);
bc.SetBool(DESC_SCALEH, true);
bc.SetString(DESC_NAME, "Link Field"_s);
if (!description->SetParameter(cid, bc, 1000))
return TRUE;
}
flags |= DESCFLAGS_DESC::LOADED;
return SUPER::GetDDescription(node, description, flags);
}
Bool HairRenderingTag::Message(GeListNode* node, Int32 type, void* data)
{
BaseDocument* doc = node->GetDocument();
if (!doc)
return TRUE;
BaseContainer* dataInstance;
BaseTag* tag = static_cast<BaseTag*> (node);
if (tag == nullptr)
return TRUE;
dataInstance = tag->GetDataInstance();
if (!dataInstance)
return TRUE;
if (type == MSG_DRAGANDDROP)
{
DragAndDrop* dnd = static_cast<DragAndDrop*>(data);
if (!dnd)
return TRUE;
// Drag & drop will only accept tags of the plugin type
if (dnd->type == DRAGTYPE_ATOMARRAY)
{
AtomArray* dndid = static_cast<AtomArray*>(dnd->data);
AutoAlloc<AtomArray> dndidarr;
dndid->CopyTo(dndidarr);
if (dndidarr->GetCount() <= 0)
return TRUE;
if (dndidarr->GetIndex(0) != nullptr)
{
if (dndidarr->GetIndex(0)->GetType() != TESTPLUGIN_ID)
return TRUE;
}
else
{
return TRUE;
}
BaseObject* tagsobject = tag->GetObject();
BaseContainer state;
if (!GetInputState(BFM_INPUT_MOUSE, BFM_INPUT_MOUSELEFT, state))
return TRUE;
// Only run when the mouse is released
if (state.GetInt32(BFM_INPUT_VALUE) == 0)
{
BaseTag *attachedTag = (BaseTag*)dndidarr->GetIndex(0);
doc->AddUndo(UNDOTYPE::CHANGE, tag);
dataInstance->SetLink(1001, attachedTag);
return TRUE;
}
}
}
return SUPER::Message(node, type, data);
}
Bool RegisterRenderingTag()
{
return RegisterTagPlugin(TESTPLUGIN_ID, "Link Test"_s, TAG_MULTIPLE | TAG_VISIBLE, HairRenderingTag::Alloc, "Thairsdkrendering"_s, AutoBitmap("hairrendering.tif"_s), 0);
}
I'm not sure if I am doing something fundementally wrong with the undos since I have had undos of other types working in other plugins.
Any help would be greatly appreciated.
John Terenece
Thanks for the response.
That's what I figured would be the case.
I'm looking to have my plugin randomize parameters on an object specified by a user. It would help if I could use the active parameter that the user has selected instead of needing to have them directly feed in parameters.
Hello.
I'm currently working on a simple plugin that will change the controls of the active object in the scene. I know how to get the active object in the scene since that is simple enough.
My question is is it possible to get from an object/tag which, if any, of its parameters are currently selected/highlighted like in the picture below.
From looking through the sdk I didn't come across anything that would indicate whether it was possible or not definitively.
I assume that you can't but I would like to be able to be sure.
Any help would be greatly appreciated.
John Terenece
Thanks for the response, that seems to have solved the problem.
Hello.
I am trying to create a FieldLayer plugin that would utilize global variables which would be used to effect the final values returned via Sample, for example an array to affect the final values. The purpose of using global variables is to keep the amount of overhead at a minimum for everytime Sample is called.
My issue comes in that the global variables are giving inconsistent results, including resetting to their default value. I've looked at the information in the sdk and the code examples and didn't find anything that explained why this would be happening, granted I might have completely missed something.
Here is a simplified version of my code using the HueShiftFieldLayer example. I know the code doesn't make sense for a proper plugin I'm just using it to highlight the problem I'm encountering.
#include "c4d.h"
#include "c4d_symbols.h"
#include "c4d_falloffplugin.h"
#include "main.h"
#include "c4d_fieldplugin.h"
class HueShiftFieldLayer : public FieldLayerData
{
INSTANCEOF(HueShiftFieldLayer, FieldLayerData)
public:
static NodeData* Alloc()
{
iferr(NodeData * const result = NewObj(HueShiftFieldLayer))
{
DebugStop();
return nullptr;
}
return result;
}
virtual maxon::Result<void> InitSampling(FieldLayer& layer, const FieldInfo& info, FieldShared& shared);
virtual void FreeSampling(FieldLayer& layer, const FieldInfo& info, FieldShared& shared);
virtual Bool IsEqual(const FieldLayer& layer, const FieldLayerData& comp) const;
virtual maxon::Result<void> Sample(const FieldLayer& layer, const FieldInput& inputs, FieldOutputBlock& outputs, const FieldInfo& info) const;
virtual void IncrementRun();
virtual Bool Message(GeListNode* node, Int32 type, void* t_data);
Int32 incrementUp = 0;
private:
Float _offset = 0.0_f;
};
Bool HueShiftFieldLayer::Message(GeListNode* node, Int32 type, void* t_data)
{
BaseDocument* doc = node->GetDocument();
if (!doc)
return TRUE;
BaseContainer *dataInstance = ((BaseList2D*)node)->GetDataInstance();
if (!dataInstance)
return TRUE;
IncrementRun();
// I would expect this number to keep incrementing up, instead it seems to almost randomly reset to 0 or continue from previous values
ApplicationOutput("Count " + String::IntToString(incrementUp));
return TRUE;
}
void HueShiftFieldLayer::IncrementRun()
{
incrementUp++;
}
maxon::Result<void> HueShiftFieldLayer::InitSampling(FieldLayer& layer, const FieldInfo& info, FieldShared& shared)
{
return maxon::OK;
}
void HueShiftFieldLayer::FreeSampling(FieldLayer& layer, const FieldInfo& info, FieldShared& shared)
{
}
Bool HueShiftFieldLayer::IsEqual(const FieldLayer& layer, const FieldLayerData& comp) const
{
return true;
}
maxon::Result<void> HueShiftFieldLayer::Sample(const FieldLayer& layer, const FieldInput& inputs, FieldOutputBlock& outputs, const FieldInfo& info) const
{
return maxon::OK;
}
Bool RegisterHueTest()
{
const Bool registerFieldLayerSuccess = RegisterFieldLayerPlugin(789456,
"Hue Shift Layer"_s,
"Changes the color hue."_s,
"Hue Shift Layer"_s,
FIELDLAYER_HASREMAP,
HueShiftFieldLayer::Alloc,
"Flhueshift"_s,
nullptr,
0,
nullptr);
if (registerFieldLayerSuccess == false)
return FALSE;
else
return registerFieldLayerSuccess;
}
I tried doing the same global variable as above in a FieldObject plugin and I got the results that I would expect with my variable just incrementing up everytime Message is called.
Are global variables not able to be maintained inside of a FieldLayer plugin?
Any help would be greatly appreciated.
John Terenece
Thanks, that was exactly what I was looking for.
John Terenece
Hi.
I'm working on a plugin that utilizes a FieldList control that I am creating using custom data type CUSTOMDATATYPE_FIELDLIST. My issue comes in with the "Enable/Disable Value Clamping" control and not being able to change it using code.
I've looked through the sdk in relation to the FieldList entry and couldn't find anything that refrences it. The closest thing I could find was the FIELDLIST_FLAGS::CLAMPOUTPUT which not suprisingly had no effect on the control when used with SetFlags.
This is my code for creating my FieldList control.
DescID DescFieldGroup = DescLevel(idFieldGroup, DTYPE_GROUP, 0);
if (!singleid || DescFieldGroup.IsPartOf(*singleid, NULL))
{
BaseContainer bc;
bc = GetCustomDataTypeDefault(DTYPE_GROUP);
bc.SetInt32(DESC_COLUMNS, 1);
bc.SetBool(DESC_SCALEH, TRUE);
if (!description->SetParameter(DescFieldGroup, bc, idMainGroup))
return TRUE;
}
cid = DescLevel(idGrowField, CUSTOMDATATYPE_FIELDLIST, 0);
if (!singleid || cid.IsPartOf(*singleid, NULL))
{
BaseContainer bc;
bc = GetCustomDataTypeDefault(CUSTOMDATATYPE_FIELDLIST);
bc.SetBool(DESC_FIELDLIST_NOCOLOR, TRUE);
bc.SetBool(DESC_FIELDLIST_NODIRECTION, TRUE);
bc.SetBool(DESC_FIELDLIST_NOROTATION, TRUE);
if (!description->SetParameter(cid, bc, DescFieldGroup))
return TRUE;
}
Is it possible to change the control via code or can it only be changed manually by a user.
Any help would be greatly appreciated.
John Terenece
Thanks for the response. This seems to have solved the problem.
John Terenece
Hello.
I am encountering a problem when I insert my object plugin into a scene in R26, save the document and then reopen the file. When I click the button to open the GeDialog Cinema immediately crashes on me.
I stripped down the code for my GeDialog to the bare minimum that was still similar to the examples in the sdk and the problem is still occurring.
#include "c4d_general.h"
#include "c4d.h"
#include "c4d_symbols.h"
#include "main.h"
#define ID_RUNDIALOG 1059364
enum Controls
{
idSendMessageLinkObject = 1000,
idLinkedObject,
idGatherChildren,
idOverallScrollGroup = 2000,
idGroupToFlush,
idControls,
idLaunchDialogWindow = 1500,
idBasicControlGroup,
};
class ObjDialog : public GeDialog
{
public:
ObjDialog() {}
virtual ~ObjDialog() {}
virtual Bool CreateLayout();
virtual Bool InitValues();
virtual void DestroyWindow();
virtual Bool Command(Int32 id, const BaseContainer& msg);
virtual Int32 Message(const BaseContainer& msg, BaseContainer& result);
};
Bool ObjDialog::CreateLayout()
{
Bool res = GeDialog::CreateLayout();
SetTitle("Dialog Window"_s);
ScrollGroupBegin(idOverallScrollGroup, BFH_SCALEFIT | BFV_SCALEFIT, SCROLLGROUP_VERT | SCROLLGROUP_HORIZ);
{
GroupBegin(idGroupToFlush, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, String(), 0);
{
GroupBegin(idControls, BFH_SCALEFIT | BFV_SCALEFIT, 5, 0, String(), 0);
{
}
GroupEnd();
}
GroupEnd();
}
GroupEnd();
return res;
}
Bool ObjDialog::InitValues()
{
if (!GeDialog::InitValues())
return false;
return true;
}
void ObjDialog::DestroyWindow() {}
Bool ObjDialog::Command(Int32 id, const BaseContainer& msg)
{
return true;
}
Int32 ObjDialog::Message(const BaseContainer& msg, BaseContainer& result)
{
return GeDialog::Message(msg, result);
}
class RunDialog : public ObjectData
{
public:
virtual Bool Init(GeListNode* node);
virtual BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp* hh);
virtual Bool Message(GeListNode* node, Int32 type, void* t_data);
virtual Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags);
static NodeData* Alloc() { return NewObjClear(RunDialog); }
maxon::BaseArray<BaseObject*> objArrayToCheckForMatch;
private:
ObjDialog dlg;
};
Bool RunDialog::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
{
BaseContainer *dataInstance;
const DescID *singleid;
if (!description->LoadDescription(node->GetType()))
{
}
dataInstance = ((BaseList2D*)node)->GetDataInstance(); // Get the container for the tag
if (!dataInstance)
return FALSE;
singleid = description->GetSingleDescID();
DescID cid;
DescID DescHelperControlGroup = DescLevel(idBasicControlGroup, DTYPE_GROUP, 0);
if (!singleid || DescHelperControlGroup.IsPartOf(*singleid, NULL))
{
BaseContainer bc;
bc = GetCustomDataTypeDefault(DTYPE_GROUP);
bc.SetString(DESC_NAME, "Run Dialog"_s);
bc.SetInt32(DESC_COLUMNS, 1);
bc.SetInt32(DESC_DEFAULT, 1);
bc.SetBool(DESC_SCALEH, TRUE);
if (!description->SetParameter(DescHelperControlGroup, bc, DescLevel(ID_RUNDIALOG)))
return TRUE;
}
cid = DescLevel(idLaunchDialogWindow, DTYPE_BUTTON, 0);
if (!singleid || cid.IsPartOf(*singleid, NULL)) // important to check for speedup c4d!
{
BaseContainer bc;
bc = GetCustomDataTypeDefault(DTYPE_BUTTON);
bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON);
bc.SetString(DESC_NAME, "Open Dialog"_s);
if (!description->SetParameter(cid, bc, DescHelperControlGroup))
return TRUE;
}
flags |= DESCFLAGS_DESC::LOADED;
return TRUE;
}
Bool RunDialog::Init(GeListNode* node)
{
return true;
}
Bool RunDialog::Message(GeListNode* node, Int32 type, void* t_data)
{
if (type == MSG_DESCRIPTION_COMMAND)
{
DescriptionCommand *dc = (DescriptionCommand*)t_data;
if (!dc)
return TRUE;
Int32 ID = Int32(dc->_descId[0].id);
BaseDocument* doc = node->GetDocument();
if (!doc)
return TRUE;
switch (ID)
{
// Problem occurs here
case idLaunchDialogWindow:
{
if (dlg.IsOpen() == false)
dlg.Open(DLG_TYPE::ASYNC, ID_RUNDIALOG, -1, -1, 300, 200, 0, OPENDIALOGFLAGS::IGNORELAYOUT);
break;
}
}
}
return true;
}
BaseObject* RunDialog::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh)
{
BaseObject *nullReturn = BaseObject::Alloc(Onull);
return nullReturn;
}
Bool RegisterRunDialog()
{
return RegisterObjectPlugin(ID_RUNDIALOG, "Run Dialog"_s, OBJECT_GENERATOR | OBJECT_INPUT, RunDialog::Alloc, "ORunDialog"_s, AutoBitmap("atom.tif"_s), 0);
}
I've used this same code in R20 and R25 and haven't had a single issue with opening the GeDialog, I only started encountering the problem in R26.
Looking at the examples in the sdk that deal with GeDialogs they are all opened via a CommandData plugin and the GeDialog manual specifically mentions that GeDialog based windows are typically owned by a CommandData plugin.
Is it now a requirement to use a CommandData plugin to open a GeDialog?
Any help would be greatly appreciated.
John Terenece