3d Connector problem after 2024.4 update
-
Hi.
After 2024.4 I got a message from the customer that now my plugin broken.
It seems the old Dynamic Connector was rewritten in the 2024.4 release and was significantly changed. But the release page says: "Legacy Connectors will be compatible".I've realized that the Dynamic Connector which my plugin creates is not compatible anymore.
F.e. Instead of legacy FORCE_TYPE DescID now there are two DescIDs: FORCE_TYPE_GROUP and FORCE_TYPE_MODE.
It is pretty frustrating, because now all legacy force_types (like Fixed, Slider, Ragdoll, Hinge, etc) have different ID values. The "Fixed" was 6 before the 2024.4. Now it is 0. Etc.I've rewrote my plugin in part of FORCE_TYPE and now my plugin creates correct force_types. But now I can't understand what is going on with the old OBJECT_A and OBJECT_B settings of the Connector? In previous versions we were able to add the Joint object here. Now I can't. I am trying to drag and drop my character's joint into the new "Objects" field but nothing happens.
My plugin really depends on the Joint object in this field.
Is it not possible to use Joint objects with the Bullet Dynamic tag as objects which Connector connects anymore?
If yes, then legacy Connectors are not compatible anymore.If I am not right and Joint object is still valid I can't understand how this code should look in a new version?
connector->SetParameter(ConstDescID(DescLevel(FORCE_OBJECT_A)), objectA, DESCFLAGS_SET::NONE); connector->SetParameter(ConstDescID(DescLevel(FORCE_REFERENCE_A)), 2, DESCFLAGS_SET::NONE);
-
Hello @yaya,
Thank you for reaching out to us. Let me start with stating that we are committed to maintaining ABI compatibility within a major version. I.e., developers should not be required to recompile their plugins outside of major version changes.
edit: I think I see the regression now, stay tuned.
To help us get this issue solved quickly, could you please:
- Provide an example scene with the issue, i.e., show us the pre 2024.4 setup that is not reproducible anymore.
- Provide us with more source code. Please show us a substantial portion of your non-working legacy code.
- If necessary, it could be beneficial to provide us with a temporary license for your plugin, so that we can run and debug it.
You can reach out to us in a more confidential manner via
sdk_support(at)maxon(dot)net
.Cheers,
Ferdinand -
Hello @yaya,
First of all, my apologies for the problems this issue caused you.
I am fairly sure that I found the issue after talking with the developer and writing some code. The issue seems to lie in the dynamic handling of the cycle values of
FORCE_TYPE
. I have sent out a mail internally, so that we can decide how we are going to deal with this issue. I will update this thread in the next week, hopefully on Tuesday.Cheers and have a nice weekend,
Ferdinand -
-
Dear Community,
this issue will be fixed in the upcoming Cinema 4D release. Plugins and code that target the old interface, will then continue to work as before. We will not fix 2024.4.0 in this case.
Ther other problem is here that we used here an internal GUI type,
ItemTreeData
and also quite a few other proprietary things. Below you can find a code example for how to interact with the new Objects field programmatically in a writing fashion, i.e., how to setup such rig. Reading such data is currently not possible. There is also quite few other stuff missing, I will keep this thread updated with our decision how we proceed here. But when decide to streamline the API here, such changes will not arrive with the next update but only with a later update.Cheers,
FerdinandResult
Project
example.itemtree.zip: Just unpack it into the
plugins
folder of an SDK, and addplugins/example.itemtree;
to the\plugins\project\projectdefintion.txt
.Code
/* example.itemtree (C) MAXON Computer GmbH, 2024 Demonstrates how to use the ItemTree data type to manage the linked nodes of a connector constraint. The code shown here is NOT version agnostic, as it targets the 2024.5 SDK. But I used the 2024.0 SDK to build this example, just to make sure that one could build a plugin which works with all versions of 2024. Author: Ferdinand Hoppe Date: 01/07/2024 */ #include "c4d_basedocument.h" #include "c4d_baseobject.h" #include "c4d_basetag.h" #include "c4d_commandplugin.h" #include "c4d_general.h" #include "c4d_plugin.h" #include "c4d_resource.h" #include "customgui_itemtree.h" #include "lib_description.h" #include "maxon/apibase.h" #include "maxon/errorbase.h" #include "dynconstraintobject.h" #include "dyninteraction.h" #include "tjointsettings.h" const Int32 PID_CMD_EXAMPLEITEMTREE = 1063776; #define Tjointsettings 1061976 // The internal ID of the Tjointsettings tag, we did not expose that. #define JOINT_OBJECT_HASH 7023 // You do not have to declare this ID when you build with the 2024.5 SDK, it is already // defined, but I did, as I was using the 2024.0 SDK. class ExampleItemTreeCommand : public CommandData { INSTANCEOF(ChangeExamplesCommand, CommandData) public: static ExampleItemTreeCommand* Alloc() { return NewObjClear(ExampleItemTreeCommand); } /// Returns the hash of the given #node. /// /// The connector code unfourntely uses here proprietary code, so we must mimic what it does internally. static maxon::HashInt GetNodeHash(const BaseList2D* const node) { if (!node) return 0; iferr_scope_handler { return 0; }; const GeMarker& marker = node->GetMarker(); void* geMarkerData = nullptr; Int32 geMarkerSize = 0; marker.GetMemory(geMarkerData, geMarkerSize); maxon::Uuid newUniqueId; maxon::UuidInterface& writableUuId = newUniqueId.MakeWritable() iferr_return; writableUuId.Set(static_cast<const UChar*>(geMarkerData), geMarkerSize) iferr_return; return newUniqueId.GetHashCode(); } /// @brief Creates a Tjointsettings tag for the #connector that targets the given #target. static BaseTag* CreateObjectTag(BaseObject* const connector, BaseObject* const target) { BaseContainer& bc = connector->GetDataInstanceRef(); BaseTag* const tag = connector->MakeTag(Tjointsettings); if (!tag) return nullptr; tag->SetName(target->GetName() + " settings"); BaseContainer& data = tag->GetDataInstanceRef(); data.SetUInt64(JOINT_OBJECT_HASH, GetNodeHash(target)); data.SetLink(JOINT_OBJECTLINK, target); data.SetLink(JOINT_OBJECT, target); data.SetInt32(JOINT_TYPE, bc.GetInt32(FORCE_TYPE)); data.SetBool(JOINT_ACTIVE, true); data.SetInt32(JOINT_REFERENCE, FORCE_REFERENCE_X); data.SetInt32(JOINT_APPLICATION, FORCE_APPLICATION_COM); data.SetInt32(JOINT_POINT, 0); data.SetFloat(JOINT_POINT_SELECTION_SHAPE_CONSERVATION, 1.0_f); data.SetFloat(JOINT_POINT_SELECTION_SHAPE_CONSERVATION_DAMPING, 0.2_f); data.SetFloat(JOINT_REGION, 0.2_f); data.SetBool(JOINT_PIN_TO_WORLD, false); return tag; } /// @brief Returns the Tjointsettings tag of the #connector that targets the given #target. static BaseTag* GetObjectTag(BaseObject* const connector, BaseObject* const target) { BaseTag* tag = connector->GetFirstTag(); maxon::HashInt hash = GetNodeHash(target); for (; tag; tag = tag->GetNext()) { if (tag->GetType() == Tjointsettings && tag->GetDataInstance()->GetUInt64(JOINT_OBJECT_HASH) == hash) return tag; } } /// @brief Runs the example of how to use the ItemTree data type, specifically for the connector constraint. maxon::Result<void> RunExample(BaseDocument* const doc) { if (!doc) return maxon::NullptrError(MAXON_SOURCE_LOCATION, "Invalid document"_s); // Declare the objects we will create and define the scope handler to clean up in case of an error. BaseObject* connector = nullptr; BaseObject* joint0 = nullptr; BaseObject* joint1 = nullptr; iferr_scope_handler { if (joint0) { joint0->Remove(); BaseObject::Free(joint0); } if (joint1) { joint1->Remove(); BaseObject::Free(joint1); } if (connector) { connector->Remove(); BaseObject::Free(connector); } return err; }; // Allocate the connector and two joints, insert them into the document and set their positions. connector = BaseObject::Alloc(Oconnectorconstraint); joint0 = BaseObject::Alloc(Ojoint); joint1 = BaseObject::Alloc(Ojoint); if (!connector || !joint0 || !joint1) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not allocate node."_s); doc->InsertObject(connector, nullptr, nullptr); doc->InsertObject(joint0, nullptr, nullptr); doc->InsertObject(joint1, joint0, nullptr); joint0->SetMg(MatrixMove(Vector(0, 0, -100))); joint1->SetMg(MatrixMove(Vector(0, 0, +100))); // Get the item tree from the connector and insert the two joints. GeData data; if (!connector->GetParameter(ConstDescID(DescLevel(CONSTRAINT_OBJECT_LIST)), data, DESCFLAGS_GET::NONE)) return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION, "Could not get parameter."_s); ItemTreeData* const itemTree = static_cast<ItemTreeData*>(data.GetCustomDataTypeWritableI(CUSTOMDATA_ITEMTREE)); if (!itemTree->AddItem(0, joint0->GetName(), joint0, GeData(), ITEMTREE_FLAG_ENABLE) || !itemTree->AddItem(1, joint0->GetName(), joint1, GeData(), ITEMTREE_FLAG_ENABLE)) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not add joints to item tree."_s); if (!connector->SetParameter(ConstDescID(DescLevel(CONSTRAINT_OBJECT_LIST)), data, DESCFLAGS_SET::USERINTERACTION)) return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION, "Could not set parameter."_s); // This is how we would get existing tags, but this does not work at the moment, you either need an internal // message type or let Cinema 4D chew on the scene for the data tags to be created automatically. // BaseTag* const tag0 = GetObjectTag(connector, joint0); // BaseTag* const tag1 = GetObjectTag(connector, joint1); // Instead we create the tags manually. BaseTag* const tag0 = CreateObjectTag(connector, joint0); BaseTag* const tag1 = CreateObjectTag(connector, joint1); if (!tag0 || !tag1) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not get or create setting tags."_s); // Set "Pin to World" to true for both joints. tag0->SetParameter(ConstDescID(DescLevel(JOINT_PIN_TO_WORLD)), GeData(true), DESCFLAGS_SET::USERINTERACTION); tag1->SetParameter(ConstDescID(DescLevel(JOINT_PIN_TO_WORLD)), GeData(true), DESCFLAGS_SET::USERINTERACTION); return maxon::OK; } /// @brief Executes the example command. Bool Execute(BaseDocument* doc, GeDialog* parentManager) { iferr_scope_handler { ApplicationOutput("Execute: @", err); return false; }; RunExample(doc) iferr_return; EventAdd(); return true; } }; // The module boilerplate code to register plugins, nothing out of the ordinary to see below. Bool PluginStart() { if (!RegisterCommandPlugin( PID_CMD_EXAMPLEITEMTREE, "Run ItemTree Example"_s, PLUGINFLAG_SMALLNODE, nullptr, "Runs the ItemTree example."_s, ExampleItemTreeCommand::Alloc())) { ApplicationOutput("Failed to register @", "ChangeExamplesCommand"_s); return false; } return true; } void PluginEnd() { } Bool PluginMessage(::Int32 id, void* data) { switch (id) { case C4DPL_INIT_SYS: { if (!g_resource.Init()) return false; return true; } } return false; }
-
Hi Ferdinand,
So to be clear, the only thing that can be done with ItemTreeData is adding new objects to it?
Thanks,
Dan -
Hey @d_schmidt,
currently, yes, you cannot read back data because you lack the item tree node type. But we finally decided that we will expose the whole UI, i.e., document it and pull out the missing parts (there is also other stuff missing). But this will probably still take a few months before it arrives in the public API.
Cheers,
Ferdinand -
Dear community,
The core issue of the ABI regression in the connector object has been fixed in the 2024.5.0 SDK Release. The 2024.5 release is again ABI compatible to 2024.0.0 SDK binaries.
Exposing
ItemTreeNode
and the missing callbacks and messages did not make it into this release (that was also never the plan). This will be done in a future release.Cheers,
Ferdinand