• Plugin is evaluated for all frames

    Cinema 4D SDK r21 s22 r23 r20 python
    2
    0 Votes
    2 Posts
    451 Views
    ferdinandF
    Hi @pyr, thank you for reaching out to us. I am afraid we will need a little bit more than that, as otherwise it will be very hard to answer your question What kind of plugin do you implement? What does it do or what would you consider as running? On a very abstract level you cannot really prevent plugins from running other than not registering them at the start of the application. But you can of course modify their output depending on basically everything, including render events and at what time offset some given document is. Cheers, Ferdinand
  • Wrong random clone displayed issue

    Cinema 4D SDK python r23 s22
    11
    1
    0 Votes
    11 Posts
    2k Views
    ferdinandF
    Hi @jochemdk, without further questions or feedback, we will consider this thread as solved by Monday and flag it accordingly. Cheers, Ferdinand
  • 0 Votes
    5 Posts
    540 Views
    fwilleke80F
    Hi Manuel, thanks for getting back to me! The size of the bitmap you want to display ? It's 320x116 pixels. Do you want to bitmap to scale when you scale the GeDialog ? No. I rather want the dialog to be just wide enough to fit the bitmap in it. And that seems to happen automatically. Even if I open the dialog with a width of e.g. 100 px, it doesn't get any smaller than (width_of_bitmap + strange_gap * 2). Is it possible that it's a macOS-only issue? Maybe it's the radius of the windows' round corners that gets added to the size. Anyway, maybe this issue isn't worth spending more of your time on. Since it works with a ASYNC_POPUPEDIT style dialog, I think I can live with what I got.
  • Find BaseMaterial that owns a BaseShader?

    Cinema 4D SDK r20 r21 r23 s22 c++
    4
    0 Votes
    4 Posts
    492 Views
    ManuelM
    hi, Confirmed, thanks @mp5gosu. That also work if the shader is inside another shader. Cheers, Manuel
  • 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
  • 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
  • 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
    631 Views
    fwilleke80F
    Nice, Manuel, thank you! I guess, I'll try the SetDParameter() approach, too, in good time. Cheers, Frank
  • UVW values inconsistency

    Cinema 4D SDK r21 s22 c++ python
    4
    0 Votes
    4 Posts
    895 Views
    rsodreR
    @r_gigante good to know, thanks!
  • Crash in ChannelShader plugin

    Cinema 4D SDK s22 c++ macos
    9
    1
    0 Votes
    9 Posts
    778 Views
    fwilleke80F
    I’m confident they will find out Thanks for everything!
  • Something to watch out for with GetAllAssets

    Cinema 4D SDK s22 c++
    2
    2 Votes
    2 Posts
    463 Views
    M
    Hi Thanks for the extra warning, just in case it was stated in the S22 API change. Cheers, Maxime.
  • 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
  • 0 Votes
    5 Posts
    754 Views
    M
    Hi Frank, sorry for coming late to the party, the API changed within the R21 where by default the icon is not more filled because it's up to you to fill it with what you need. Just in case for other people, not interested in C++ (since the manual is pretty obvious) there is a Python example in Where you can have custom icons, you can find an example of how to achieve custom icon within Python in py-custom_icon_r21.pyp. Cheers, Maxime.
  • 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
  • How to refresh a deleted object link in Python?

    Cinema 4D SDK
    6
    0 Votes
    6 Posts
    1k Views
    X
    Hi: It feels like a good question, otherwise I wouldn't have known that XPresso had this problem.I think your problem is that XPresso Tag adds links to user data.If you add user data links to Python nodes, you will have no problem putting splines in the links.Or drag the spline directly to XPresso manager, output the object, there will be no problem. import c4d #Welcome to the world of Python def main(): #global spline global obj global time spline = op[c4d.ID_USERDATA,1] if spline != None: SplineHelp = c4d.utils.SplineHelp() SplineHelp.InitSpline(spline) offset = SplineHelp.GetOffsetFromReal(time, 0) pos = SplineHelp.GetPos(offset) #print(pos) obj.SetAbsPos(pos) [image: 1598474349417-7854567857885.jpg]
  • 0 Votes
    7 Posts
    1k Views
    fwilleke80F
    Hmmmm, well, on the one hand this function is a nice thing: It really does do the escaping for me, so the spaces in the URL are correctly replaced with "%20". But it does not handle anchors in a URL. This URL works fine: file:///Applications/Maxon/Cinema 4D R22/plugins/myplugin/help/content/omyobject/omyobject.html But this URL is not opened, instead an error (code -43) is thrown: file:///Applications/Maxon/Cinema 4D R22/plugins/myplugin/help/content/omyobject/omyobject.html#MYOBJECT_ATTRIBUTE That is a pity.
  • 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.