• 0 Votes
    3 Posts
    432 Views
    fwilleke80F
    Hi Manuel, thanks for the info! If smooth is set to false, the wrong positions disappear in deed. The distribution of the positions, however, is much uglier. It requires the user to change some specific attributes of the spline to make it look nice. But I think, that's ok for now. Thanks again & greetings, Frank
  • 0 Votes
    5 Posts
    691 Views
    fwilleke80F
    Thanks for confirming! The strange thing is that I can reproduce this even in R20. Seems, not many people use this function to open URLs. I'll mark this as solved then, as my solution presented in this thread works flawlessly on all R2x releases. Cheers, Frank
  • Error reading resource Line 1

    Cinema 4D SDK r20 c++ python windows
    3
    0 Votes
    3 Posts
    903 Views
    B
    After a LOT of testing around I finally found the culprit. The header file was encoded UTF-8 with BOM. I finally figured it out after noticing that two versions of the header file that worked or didn't work had a 3 byte difference in file size. No idea how that snuck in there, but now it finally works. Also now it makes sense that the error was pointing to line 1.
  • 0 Votes
    19 Posts
    2k Views
    ManuelM
    hi, never say never. Sorry for the delay, this is a "unusual" case ^^ I did try several options (observable, notification, message etc) I finished using the function AddEventNotification witch is private. (so i had to figure out how it was working) The shader and the viewport is updated but with a small latency. We can try to improve that solution if you want or you can go with your own. Below the code for both the ObjectData and the Shader. The shader need to register to be notified. (this is automatic, nothing to add on the ObjectData's side) Than you need to check the message in Message I feel that the important part is to remove the notification and re/add it. (not sure what it can do if you don't do it ^^) class pc12019_object : public ObjectData { INSTANCEOF(pc12019_object, ObjectData); public: static NodeData* Alloc() { return NewObjClear(pc12019_object); } BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) override { return BaseObject::Alloc(Ocube); } Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags) override { if (!description->LoadDescription(Obase)) return false; const DescID* singleid = description->GetSingleDescID(); const DescID cid = DescLevel(OBJ_COLOR, DTYPE_COLOR, 0); if (!singleid || cid.IsPartOf(*singleid, nullptr)) { BaseContainer bc = GetCustomDataTypeDefault(DTYPE_COLOR); bc.SetString(DESC_NAME, "Color"_s); bc.SetData(DESC_GUIOPEN, true); description->SetParameter(cid, bc, DescLevel(ID_OBJECTPROPERTIES)); } flags |= DESCFLAGS_DESC::LOADED; return SUPER::GetDDescription(node, description, flags); } }; class pc12019_shader : public ShaderData { INSTANCEOF(pc12019_shader, ShaderData); public: static NodeData* Alloc() { return NewObjClear(pc12019_shader); } Vector Output(BaseShader* sh, ChannelData* cd) override { return color; } Bool Read(GeListNode* node, HyperFile* hf, Int32 level) override { // need to read the color return true; } Bool Write(GeListNode* node, HyperFile* hf) override { // need to write the color return true; } Bool CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn) override { pc12019_shader* destShader = static_cast<pc12019_shader*>(dest); destShader->color = color; return true; } Bool GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags) override { if (!description->LoadDescription(Xbase)) return false; const DescID* singleid = description->GetSingleDescID(); DescID cid = DescLevel(SH_OBJECTLINK, DTYPE_BASELISTLINK, 0); if (!singleid || cid.IsPartOf(*singleid, nullptr)) { BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BASELISTLINK); bc.SetString(DESC_NAME, "Link"_s); bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_LINKBOX); BaseContainer ac; ac.SetInt32(Obase, 1); bc.SetContainer(DESC_ACCEPT, ac); description->SetParameter(cid, bc, DescLevel(ID_SHADERPROPERTIES)); } flags |= DESCFLAGS_DESC::LOADED; return SUPER::GetDDescription(node, description, flags); } INITRENDERRESULT InitRender(BaseShader* sh, const InitRenderStruct& irs) override { iferr_scope_handler { err.DiagOutput(); return INITRENDERRESULT::UNKNOWNERROR; }; if (irs.doc == nullptr) return INITRENDERRESULT::OK; BaseContainer* bcShader = sh->GetDataInstance(); if (bcShader == nullptr) return INITRENDERRESULT::UNKNOWNERROR; BaseObject * linkedObject = bcShader->GetObjectLink(SH_OBJECTLINK, irs.doc); if (linkedObject == nullptr) return INITRENDERRESULT::OK; RemoveNotification(sh, linkedObject); AddNotification(sh, linkedObject); GeData data; linkedObject->GetParameter(OBJ_COLOR, data, DESCFLAGS_GET::NONE); color = data.GetVector(); return INITRENDERRESULT::OK; } Bool Init(GeListNode* node) override { BaseMaterial* mat = static_cast<BaseMaterial*>(node); BaseContainer* bc = mat->GetDataInstance(); bc->SetLink(SH_OBJECTLINK, nullptr); return true; } void Free(GeListNode* node) override { GeData data; BaseObject* op = static_cast<BaseObject*>(node); if (op == nullptr) return; BaseDocument* doc = node->GetDocument(); if (doc == nullptr) return; op->GetParameter(SH_OBJECTLINK, data, DESCFLAGS_GET::NONE); BaseList2D* linkedObject = data.GetLink(doc); RemoveNotification(node, linkedObject); } Bool RemoveNotification(GeListNode* node, BaseList2D* linkedObject) { if ((node == nullptr) || (linkedObject == nullptr)) return false; BaseObject* op = static_cast<BaseObject*>(node); if (op == nullptr) return false; BaseDocument* doc = op->GetDocument(); if (doc == nullptr) return false; if (linkedObject->FindEventNotification(doc, op, NOTIFY_EVENT::CACHE)) { return linkedObject->RemoveEventNotification(doc, op, NOTIFY_EVENT::CACHE); } return true; } Bool AddNotification(GeListNode* node, BaseList2D* linkedObject) { BaseObject* op = static_cast<BaseObject*>(node); if (op == nullptr) return false; BaseDocument* doc = op->GetDocument(); if (doc == nullptr) return false; if (!linkedObject->FindEventNotification(doc, op, NOTIFY_EVENT::MESSAGE)) { return linkedObject->AddEventNotification(op, NOTIFY_EVENT::MESSAGE, NOTIFY_EVENT_FLAG::NONE, nullptr); } return true; } Bool Message(GeListNode* node, Int32 type, void* data) override { if (type == MSG_NOTIFY_EVENT) { NotifyEventData *eventData = static_cast<NotifyEventData*>(data); // as we can add several notification we check if we received the right one if (eventData->eventid == NOTIFY_EVENT::MESSAGE) { NotifyEventMsg* eventMsg = static_cast<NotifyEventMsg*>(eventData->event_data); // Checks if the message that the object is fowarding us if the right one. (MSG_DESCRIPTION_POSTSETPARAMETER) if (eventMsg->msg_id == MSG_DESCRIPTION_POSTSETPARAMETER) { DescriptionPostSetValue* dpsv = static_cast<DescriptionPostSetValue*>(eventMsg->msg_data); const Int32 id = dpsv->descid[0][0].id; // Checks if the parameter that have been change is part of the message. if (id == OBJ_COLOR) { // ask the shader to update itself. node->SetDirty(DIRTYFLAGS::ALL); node->Message(MSG_DESCRIPTION_POSTSETPARAMETER); // this will trigger the mat update and the viewport update. (with a small delay) } } } return true; } return SUPER::Message(node, type, data); } private: Vector color = Vector(1); }; Cheers, Manuel
  • OBJECT_INPUT Child Updating

    Cinema 4D SDK sdk r20 c++
    13
    0 Votes
    13 Posts
    2k Views
    r_giganteR
    Hi @JohnThomas , the data passed in the method is actually carrying the destination object. The code should look like ... if (type == MSG_EDITABLE_END) { BaseObject* dstObject = static_cast<BaseObject*>(data); if (!dstObject) return false; BaseObject* child = dstObject->GetDown(); if (child) { child->Remove(); BaseObject::Free(child); } } ... Cheers, R
  • FieldList, and how to properly use it

    Cinema 4D SDK r20 r21 s22 r23 c++
    8
    0 Votes
    8 Posts
    953 Views
    fwilleke80F
    Thank you!
  • Compare only parts of two BaseContainers()

    Cinema 4D SDK r20 r21 s22 r23 c++
    6
    0 Votes
    6 Posts
    632 Views
    fwilleke80F
    Nice, Manuel, thank you! I guess, I'll try the SetDParameter() approach, too, in good time. Cheers, Frank
  • Edge To Spline Modeling Command

    Cinema 4D SDK sdk r20 c++
    3
    0 Votes
    3 Posts
    914 Views
    J
    Thanks for the response, that was exactly what I needed. John Thomas
  • Retrieving Viewport Camera Matrix

    Cinema 4D SDK r20 sdk c++
    9
    0 Votes
    9 Posts
    2k Views
    J
    Thanks for the detailed response @r_gigante, sorry about the late post. I wasn't trying to move an object into the camera view, I was just trying to get the information about a cameras perspective so that if necessary I could create an object that would have an orientation parallel to it. John Thomas
  • Parameter Description Access not working as expected

    Cinema 4D SDK python r20
    14
    0 Votes
    14 Posts
    2k Views
    ferdinandF
    Hello @davidtorno, without any further questions or replies, we will consider this topic to be solved by Monday and flag it accordingly. Thank you for your understanding, Ferdinand
  • Overlaying icons (like Alembic)

    Cinema 4D SDK r20 r21 s22 c++ classic api
    9
    1
    0 Votes
    9 Posts
    1k Views
    fwilleke80F
    OK, I got it. Once it's all figured out, the solution seems laughably simple. In case anybody else needs to maintain R20 compatibility and wants to use custom icon overlays, here's a solution: In MyObject class declaration: (R20 code) class MyObject : public ObjectData { INSTANCEOF(MyObject, ObjectData); // ... private: #if API_VERSION < 21000 BaseBitmap *_customIcon; ///< Used to store a node's custom icon in R20 #endif }; public: #if API_VERSION < 21000 MyObject() : _customIcon(nullptr) { } ~ MyObject() { if (_customIcon) BaseBitmap::Free(_customIcon); } #endif In MyObject::Message(): (R20 code) const BaseContainer &dataRef = op->GetDataInstanceRef(); // // 1. Copy default icon into customIcon // // Load default icon IconData defaultIconData; if (!GetIcon(node->GetType(), &defaultIconData)) return false; // Free _customIcon if it has been allocated before, because // it will now receive the pointer to a newly allocated BaseBitmap. if (_customIcon) BaseBitmap::Free(_customIcon); // Get the actual bitmap that is our icon _customIcon = defaultIconData.GetClonePart(); if (!_customIcon) return false; // // 2. Blit overlay into customIcon // // Load overlay icon IconData overlayIcon; const Int32 overlayIconId = MyFunctionToGetTheIconId(); if (overlayIconId == NOTOK) return false; const Bool overlayIconloaded = GetIcon(overlayIconId, &overlayIcon); if (overlayIconloaded) { BlitOverlayBitmap(overlayIcon.bmp, _customIcon, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 32, 32); // // 3. Set cid->dat to use customIcon // cid->dat->bmp = _customIcon; cid->dat->x = 0; cid->dat->y = 0; cid->dat->w = _customIcon->GetBw(); cid->dat->h = _customIcon->GetBh(); cid->filled = true; } And BlitOverlayBitmap(), a variation of your BlitOverlayIcon(): void BlitOverlayBitmap(BaseBitmap *overlay, BaseBitmap *dest, Int32 x, Int32 y, Int32 bw, Int32 bh, Int32 xOffset, Int32 yOffset) { if (!overlay || !dest) return; BaseBitmap *overlayAlpha = overlay->GetInternalChannel(); BaseBitmap *destAlpha = dest->GetInternalChannel(); const Int32 destW = dest->GetBw(); const Int32 destH = dest->GetBh(); if (bw > destW) bw = destW; if (bh + yOffset > destH) bh = destH - yOffset; if (bw + xOffset > destW) bw = destW - xOffset; UInt16 aa, a, rr, r, gg, g, bb, b; aa = a = rr = r = gg = g = bb = b = 0xff; for (Int32 py = 0; py < bh; py++) { for (Int32 px = 0; px < bw; px++) { // get color and alpha from overlay icon overlay->GetPixel(x + px, y + py, &r, &g, &b); overlay->GetAlphaPixel(overlayAlpha, x + px, y + py, &a); // get color and alpha from background icon if available dest->GetPixel(px + xOffset, py + yOffset, &rr, &gg, &bb); if (destAlpha) { dest->GetAlphaPixel(destAlpha, px + xOffset, py + yOffset, &aa); } // blend overlay color against existing icon background color Float blend = a / 255.0; dest->SetPixel(px + xOffset, py + yOffset, (Int32)Blend(rr, r, blend), (Int32)Blend(gg, g, blend), (Int32)Blend(bb, b, blend)); // use only the overlay alpha if the opacity is higher to keep the original icon complete // and only in case the background has an alpha at all if (destAlpha && aa < a) { dest->SetAlphaPixel(destAlpha, px + xOffset, py + yOffset, a); } } } } Thanks for patience & support! Cheers, Frank
  • Encrypted Loggers or Custom LoggerTypes?

    Cinema 4D SDK r20 r21 s22 c++
    7
    0 Votes
    7 Posts
    759 Views
    ManuelM
    hi, I've asked the devs. We should have everything needed to do what you want. Encrypting file is builtin with maxon::Url scheme. There's already several algorithm, you just have to specify your CryptoKey. When you create a logger to a file (or it could be a web server), you attach it to a maxon::Url, it should be automatically encrypted. But that's not working and that's why I've asked the devs. I don't know if it's a limitation or a bug. What I wanted to do was create a maxon::LoggerType::EncryptedFile(), but I don't see how to do this. as i said, the encrypted part should be done by the maxon::Url (streaming) part Cheers, Manuel
  • 0 Votes
    6 Posts
    1k Views
    fwilleke80F
    It works like a charm! Thank you again! I was surprised at how little code was required. Sharing is caring. In case anyone needs it, here's the code: #include "ge_prepass.h" #include "c4d_general.h" #include "c4d_baselinkarray.h" #include "c4d_basedocument.h" /// /// \brief Registers observers and sends messages to them. /// class Observable { public: /// /// \brief Subscribes a new observer. /// /// \param[in] observer Pointer to an AtomGoal /// \param[in] doc The document that owns the AtomGoal /// /// \return A maxon error object if anything went wrong, otherwise maxon::OK /// maxon::Result<void> Subscribe(C4DAtomGoal *observer, BaseDocument *doc); /// /// \brief Unsubscribes an observer /// /// \param[in] observer Pointer to an AtomGoal that has previously been subscribed /// \param[in] doc The document that owns the AtomGoal /// void Unsubscribe(C4DAtomGoal *observer, BaseDocument *doc); /// /// \brief Sends a messages to all subscribed observers /// /// \param[in] type Message type /// \param[in] doc The document that owns the subscribed observers /// \param[in] data Optional message data /// void Message(Int32 type, BaseDocument *doc, void *data = nullptr) const; private: BaseLinkArray _observers; }; maxon::Result<void> Observable::Subscribe(C4DAtomGoal *observer, BaseDocument *doc) { if (!observer) return maxon::NullptrError(MAXON_SOURCE_LOCATION, "Observer must not be nullptr!"_s); // Check if this observer is already registered const Int32 observerIndex = _observers.Find(observer, doc); if (observerIndex != NOTOK) return maxon::OK; // Register new observer if (!_observers.Append(observer)) { return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION, "Failed to add observer to the list!"_s); } return maxon::OK; } void Observable::Unsubscribe(C4DAtomGoal *observer, BaseDocument *doc) { if (observer && doc) { const Int32 observerIndex = _observers.Find(observer, doc); if (observerIndex != NOTOK) { _observers.Remove(observerIndex); } } } void Observable::Message(Int32 type, BaseDocument *doc, void *data) const { for (Int32 i = 0; i < _observers.GetCount(); ++i) { C4DAtomGoal *atom = _observers.GetIndex(i, doc); if (atom) { atom->Message(type, data); } } }
  • ToolPlugin Problems

    Cinema 4D SDK python s22 r21 r20 r19
    7
    2
    0 Votes
    7 Posts
    1k Views
    kbarK
    @gheyret great to hear! Looking forward to seeing what it is you are creating.
  • Decoupling source from SDK project

    Cinema 4D SDK
    7
    0 Votes
    7 Posts
    1k Views
    F
    @kbar said in Decoupling source from SDK project: Since Maxon also updates the ProjectTool when a new version of C4D comes out and it may have new flags or optimazations and changes to the project structure for XCode and Visual Studio. From what I can tell it's pretty stable, also I don't mind what someone else thinks my compiler optimisation flags should be. Maintaining your own CMake version going forward for R20, R21, S22, RXX etc... would be a massive pain and just slow you down. Not as painful as you think it might be, also not as slow. Whatever floats your boat.
  • Best way to update objects after preference change?

    Cinema 4D SDK c++ r20 r21 s22
    7
    0 Votes
    7 Posts
    1k Views
    fwilleke80F
    Oh wait, I think I found a way. In case anybody else wants to know, here it is... In the PrefsDialogObject: Bool MyPrefsDialog::SetDParameter(GeListNode *node, const DescID &id,const GeData &t_data,DESCFLAGS_SET &flags) { BaseContainer* bc = MyPlugin::GetPreferences(); if (!bc) SUPER::SetDParameter(node, id, t_data, flags); switch (id[0].id) { // If PREFS_MYPLUGIN_SOMEVALUE was changed, store value and notify plugin objects in all open documents. case PREFS_MYPLUGIN_SOMEVALUE: bc->SetInt32(PREFS_MYPLUGIN_SOMEVALUE, t_data.GetInt32()); flags |= DESCFLAGS_SET::PARAM_SET; // Iterate open documents for (BaseDocument *doc = GetFirstDocument(); doc; doc = doc->GetNext()) { // Send broadcast message to each document, use unique ID doc->MultiMessage(MULTIMSG_ROUTE::BROADCAST, MyPlugin::UNIQUE_ID_PREFS, nullptr); } GeUpdateUI(); return true; } return SUPER::SetDParameter(node, id, t_data, flags); } And then, in the plugin object: Bool MyPluginObject::Message(GeListNode *node, Int32 type, void *data) { if (type == MyPlugin::UNIQUE_ID_PREFS) { GePrint("Aha! My prefs have changed!"_s); return true; } return SUPER::Message(node, type, data); }
  • 0 Votes
    3 Posts
    520 Views
    ManuelM
    hi, Since R20, arguments have been added, and the problem is coming from the last one def HeaderClick(self, root, userdata, lColID, lChannel, bDblClk, mouseX, mouseY, ua): I've opened a bug report Cheers, Manuel
  • Merge IsolateObjects into a second document

    Cinema 4D SDK r20 python windows
    2
    0 Votes
    2 Posts
    527 Views
    ManuelM
    hi, you have to save the document to memory and use the result to merge the document. When you merge, you have to set the flags to what you want to merge, otherwise it will not merge what you want. On this example i'm merging a document with another selected document. import c4d from c4d import gui # Welcome to the world of Python # Main function def main(): obj = doc.GetActiveObject() if obj is None: return # Isolate the active object isolateDoc = c4d.documents.IsolateObjects(doc, [obj]) # Creates the Memory File Strcuture mfs = c4d.storage.MemoryFileStruct() # Sets the mfs to write mode mfs.SetMemoryWriteMode() # save the document to mfs c4d.documents.SaveDocument(isolateDoc, mfs, c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, c4d.FORMAT_C4DEXPORT) # retrieve the data data = mfs.GetData() # Set the MFS to read mode mfs.SetMemoryReadMode(data[0], data[1] ) flags = c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS # Load Document selectedFile = c4d.storage.LoadDialog() if selectedFile is None: return newDoc = c4d.documents.LoadDocument(selectedFile, flags) #Merge the isolated document with the current one. c4d.documents.MergeDocument(newDoc, mfs, flags) c4d.documents.InsertBaseDocument(newDoc) c4d.documents.SetActiveDocument(newDoc) # Execute main() if __name__=='__main__': main() Cheers, Manuel
  • Blip before Correctly Updating

    Cinema 4D SDK c++ r20 sdk
    16
    0 Votes
    16 Posts
    2k Views
    D
    @m_magalhaes Sorry about the late reply again! I went to build some simplified code to post and I might have figured it out in the process. It seems that if I correctly mark the children with AddDependence then GetCache returns the cache I expect. Having the object lower in the object manager than mine causes it to lag behind slightly, but that seems consistent with other Cinema objects. Dan
  • 0 Votes
    4 Posts
    711 Views
    ManuelM
    hi, you could also use LoadDocument lowPoly = c4d.documents.LoadDocument("preset://Optimized_Assets.lib4d/Asset_Library.c4d", c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS) As the documentation says: Similar to LoadFile() but this time the document is not put into the editors list of documents and it gives control over the loaded document. Cheers, Manuel