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 -
Hello @ferdinand
I've found that if I create the connector via the code above, I can fix the problem with the itemtree/inexdata and joint settings (reference_axis parameter) successfully.But by testing it, I've found another issue.
As I mentioned in my first message, instead of the legacy FORCE_TYPE DescID now there are two DescIDs: FORCE_TYPE_GROUP and FORCE_TYPE_MODE.
And if I am trying to set the Connector mode by them, then after the creation of the Connector the "Type" and the "Mode" of the Connector object are selected correctly, but in the viewport they are not.
F.e. I do this:connector->SetParameter(ConstDescID(DescLevel(FORCE_TYPE_GROUP)), 3, DESCFLAGS_SET::NONE); connector->SetParameter(ConstDescID(DescLevel(FORCE_TYPE_MODE)), 3, DESCFLAGS_SET::NONE);
Here I am trying to create the Connector in the Ragdoll mode.
But in the viewport I see the mode as the "Ball and Socket" mode. And it works if I hit Play as a Ball and Socket.
But in the parameters section it is selected correctly: type=Spherical, mode=Ragdoll.So, I've found this problem and then I've decided to use the old FORCE_TYPE instead of new FORCE_TYPE_GROUP and FORCE_TYPE_MODE:
connector->SetParameter(ConstDescID(DescLevel(FORCE_TYPE)), 3, DESCFLAGS_SET::NONE);
And in this case the created Connector become in the correct Ragdoll mode.
Actually it is what I need. And it works for all other modes.
...Except for a "Fixed".
The old "Fixed" mode was under the int number = 6.
The new Fixed is equal to 0.
Anyway I've found the way to fix it in my plugin. But it requires to add another new if-statements (#if API_VERSION >= 2024500) in all places where the Fixed connectors should be used .I do not know is it a bug or feature, just will leave it here to get back in 2 years and remember why I've added all that #ifs in my code
-
Hello @yaya,
Thank you for reaching out to us. Although your problem might be related to your old problem for you (as introduced by the connector object changes in 2024.4), I would say this is an entirely new issue. Please open a new thread for a new issue, as lined out in our Support Procedures.
Your problem description is also rather hard to follow, at least for me. I would recommend that you follow the pattern shown in Support Procedures: Examples - Reporting Bugs & Crashes very closely.
Cheers,
Ferdinand -
addendum, I briefly spoke with the dev:
In SetDParameter, there is code that sets the correct group automatically if force_type or force_type_mode is specified, and the other way around. One thing to keep in mind is that, when you set the parameter force_type_group, the mode is set to default value for that group, i.e., ball and socket for spherical, hinge for angular, slider for linear. This is what you see ig you play in with the dropdown menus in the UI as well. So, if one first sets force_type_mode and then force_type_group, the latter resets the mode to a possibly different value. But in the code snippet attached it looks though like the order is correct (assuming is the actual code used in the plugin).
In a way, I understand that if you are setting mode and group manually from you plugin and you know what you are doing, you are setting a combination that makes sense. But in general, to be extra safe, I decided to go with this solution, so if you change the group the mode is automatically set to something that makes sense. And, btw, also when you set the mode there is a check on the group, and if the values don't match it gets overwritten with the correct group.
so, valid options are:
- simply set force_type (as before)
- set force_type_group and force_type_mode (in this order)
- only set force_type_mode (group set automatically)
If any of these don't give the expected combination it is a bug
I think he too struggled a bit with understanding what you did concretely, so I would recommend creating a bug report as lined out above.
Cheers,
Ferdinand