About

NodeData based plugin classes can implement NodeData::Message() to receive various messages that are sent for several reasons. This page shows how to handle the most important messages.

Note
Messages sent to C4DAtom::Message() and received by NodeData::Message() are synchronous messages.

NodeData::Message() corresponds to C4DAtom::Message() and C4DAtom::MultiMessage().

Usage

Basics

NodeData::Message() is called by Cinema 4D to inform the plugin class about an event or to request some information from it. Additional data is handed over in form of a void pointer that has to be cast into the correct type depending on the message.

virtual Bool Message(GeListNode* node, Int32 type, void* data)
{
switch (type)
{
case MSG_UPDATE:
{
break;
}
}
return SUPER::Message(node, type, data);
}
Note
If the plugin class hosts a subshader HandleShaderMessage() must be used to relay certain messages to this subshader. Especially MSG_GETALLASSETS, MSG_RENAMETEXTURES and MSG_MULTI_CLEARSUGGESTEDFOLDER should be handled.

Filter

With C4DAtom::MultiMessage() it is possible to send messages to complete hierarchy or branches (see also Heads and Branches). In this case it is possible to filter certain messages so they are not sent to the child elements of the given element.

Return true to let the message pass and false to block it.

The data of this message is MessageFilter:

Data

Messages can be used to retrieve data from an entity. For generic purposes this message exists:

The data of this message is RetrievePrivateData:

// This example sends MSG_RETRIEVEPRIVATEDATA to the generator to get its internal data.
data.flags = 0;
data.data = nullptr;
// send message to retrieve private data
if (object->Message(MSG_RETRIEVEPRIVATEDATA, &data))
{
// got data
if (data.data != nullptr)
{
Int32* value = static_cast<Int32*>(data.data);
GePrint("Got value: " + String::IntToString(*value));
}
}
// This example receives a MSG_RETRIEVEPRIVATEDATA message
// and sets the pointer to the internal data.
{
RetrievePrivateData* pdata = static_cast<RetrievePrivateData*>(data);
if (pdata)
{
pdata->data = &(this->_value);
return true;
}
break;
}

Assets

Plugins use external assets typically by referencing the assets file in a Filename parameter. When the document is saved with "Save Project with Assets" or is rendered via Team Render these assets have to be collected. To collect these assets Cinema 4D sends these messages to all scene elements:

Warning
MSG_GETALLASSETS is not sent to MaterialData plugins.
// This example adds the file path stored in a parameter to the asset list.
{
AssetData* assetData = static_cast<AssetData*>(data);
// checks if only texture assets should be added
if (!assetData || assetData->flags & ASSETDATA_FLAG_TEXTURESONLY)
return true;
BaseList2D* baseList2D = static_cast<BaseList2D*>(node);
if (baseList2D)
{
// get the file name
Filename path = baseList2D->GetData().GetFilename(EXAMPLE_GENERATOR_PARAMETER_FILE);
// check if the file name is set
if (path.Content())
{
// add the file name
assetData->Add(path, nullptr);
}
}
break;
}
// This example removes the folder part of the file path of the object's parameter.
{
BaseContainer* bc = (static_cast<BaseList2D*>(node))->GetDataInstance();
if (bc)
{
Filename path = bc->GetFilename(EXAMPLE_GENERATOR_PARAMETER_FILE);
// check if the directory part of the path contains anything
if (path.GetDirectory().Content())
{
bc->SetFilename(EXAMPLE_GENERATOR_PARAMETER_FILE, path.GetFile());
}
}
break;
}

Further asset related messages are:

  • MSG_MULTI_MARKMATERIALS: When the given data is nullptr this is sent to scene elements to let them mark the materials they use (with BIT_MATMARK). Otherwise the message is sent to get material links translated, for example when a material is replaced. In this case the corresponding data is MarkMaterials. This is needed for example when an object that is using a material is copied.
  • MSG_RENAMETEXTURES: This message is sent to all scene elements when a bitmap image was renamed (e.g. by "Save Project" or by BodyPaint 3D) and all shaders etc. need to adjust the name. The message data is RenameTextureMessage.

See also BaseDocument Convert and Export and Disc I/O.

Icon

Cinema 4D sends a message to ObjectData and TagData based plugins so they can change their icon dynamically.

The data of this message is GetCustomIconData:

// This example simply sets a standard icon as the object's icon.
{
// get data
GetCustomIconData* iconData = static_cast<GetCustomIconData*>(data);
if (iconData == nullptr)
return true;
// set as filled
iconData->filled = true;
// load a standard Cinema icon
return GetIcon(RESOURCEIMAGE_OK, iconData->dat);
}

Interaction

It is possible to drag and drop something onto objects and tags in the Object Manager. These elements are informed about this event with this message.

  • MSG_DRAGANDDROP: Received by an element in the Object Manager when something is dropped on it. The corresponding data is DragAndDrop.
// This example checks if the file dropped onto an object is neither image nor scene file.
// If so, the file path will be stored in one of the object's parameters.
{
// get data
DragAndDrop* dnd = static_cast<DragAndDrop*>(data);
if (!dnd)
return false;
// ignore this when the object itself is dragged
return true;
// check if a file (scene or image) is dragged onto the object
if (dnd->type != DRAGTYPE_FILENAME_OTHER || !dnd->data)
return false;
// drop
{
// get filename
Filename* file = static_cast<Filename*>(dnd->data);
// set parameter
node->SetParameter(EXAMPLE_GENERATOR_PARAMETER_FILE, *file, DESCFLAGS_SET_0);
}
return true;
}

When the user double clicks on an icon in the Object Manger this message is sent to the corresponding element.

// This example simply opens a message dialog when one
// double-clicks on the icon in the Object Manager.
case MSG_EDIT:
{
MessageDialog("You double-clicked on this object!");
break;
}

Attribute Manager Interaction

Users edit elements and their parameters with the Attribute Manager. The Attribute Manager sends multiple messages to the edited object to inform about the interaction.

// This example just opens a message dialog when a button was pressed.
{
if (!dc)
return false;
// check the ID of the pressed button
if (dc->id[0].id == EXAMPLE_GENERATOR_BUTTON)
{
MessageDialog("You pressed the button.");
}
break;
}

Further messages are:

// This example defines the content of a popup menu of a "POPUP" parameter element.
{
DescriptionPopup* dp = static_cast<DescriptionPopup*>(data);
// check the ID of the popup element
if (dp->id[0] == EXAMPLE_GENERATOR_POPUP)
{
// if nothing is chosen
// the menu should be build
if (dp->chosen == 0)
{
menu.InsData(1, "Options");
menu.InsData(10, "Option A");
menu.InsData(20, "Option B");
dp->popup.InsData(0, menu);
}
else
{
const Int32 option = dp->chosen;
GePrint("You chose option " + String::IntToString(option));
}
}
break;
}
// This example receives a custom GUI messages sent from
// the Attribute Manager. If the messages is sent from
// a specific custom GUI type, the message data is printed.
{
if (dcgn)
{
// get parameter ID
const Int32 parameterID = dcgn->_descId[-1].id;
GePrint("Message from parameter: " + String::IntToString(parameterID));
// check the type of custom GUI
if (dcgn->_customGuiId == ID_CUSTOMGUI_COLORSTRING)
{
// check the message ID
if (dcgn->_subId == MSG_DESCRIPTION_COLORSTRING_COLORCHANGE)
{
// get data from the message
const BaseContainer* messageData = dcgn->_data;
if (messageData)
{
const Vector color = messageData->GetVector(MSG_DESCRIPTION_COLORSTRING_COLOR);
GePrint("New Color: " + String::VectorToString(color));
}
}
}
}
break;

See also Description Notifications.

These messages are related to the Take system:

Creation

  • MSG_MENUPREPARE: Allows tags, objects, shaders etc. to do some setup work when called from the menu. The corresponding data is the current BaseDocument. Is called before the object is added to the document.
// This example adds a tag to the object when it is created from the menu.
{
BaseObject* op = static_cast<BaseObject*>(node);
BaseTag* annotationTag = op->MakeTag(Tannotation);
if (annotationTag)
{
const String annotation("This is a new object.");
annotationTag->SetParameter(ANNOTATIONTAG_TEXT, annotation, DESCFLAGS_SET_0);
}
break;
}

Document Related

Certain events trigger a broadcast message that is sent to all elements of a BaseDocument.

Some messages are just send to SceneHookData plugins. See also BaseDocument::SendInfo().

// This example catches MSG_DOCUMENTINFO in a ObjectData::Message() function.
// When the document is loaded the value of a old (legacy) parameter is copied
// into a new parameter.
{
DocumentInfoData* msg = static_cast<DocumentInfoData*>(data);
if (!msg)
return false;
// switch message sub-type
switch (msg->type)
{
{
BaseObject* op = static_cast<BaseObject*>(node);
if (!op)
return false;
if (!bc)
return false;
bc->SetInt32(NEW_PARAMETER, bc->GetInt32(OLD_PARAMETER));
GePrint("document is loaded");
break;
}
}
break;
}

Also these messages are sent to all elements:

A special message is sent when the element is animated:

  • MSG_ANIMATE: Sent to elements after they have been animated. Only sent to objects with keyframes.

Further Reading