Plugin Cafe Homepage
Forum Home Forum Home > Plugin Cafe > SDK Help
  New Posts New Posts
  FAQ FAQ  Forum Search

Tag plugin

Author
Message
  Topic Search Topic Search
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Topic: Tag plugin
    Posted: 2017 Nov 13 at 6:10am

User Information:

Cinema 4D Version:   R19 
Platform:   Windows  ;   
Language(s):     C++  ;   

---------

I am using a TagData to store information for an object.
There is a reason why i don't store this information into a container in the object itself, but out of scope of this question.

I would like -inside of the tag plugin- to use GetObject() to obtain the object the tag was assigned to.
But I haven't found a way for the TagData to know when the tag is being added to the object.
At time of TagData::Init the GetObject returns a nullptr since the tag is not yet assigned.

In a scenehook I have noticed I can do following in Message():


    if (type == MSG_DOCUMENTINFO)
    {
        DocumentInfoData* did = static_cast<DocumentInfoData*>(data);
        if (did && (did->type == MSG_DOCUMENTINFO_TYPE_TAG_INSERT))
        {
            BaseTag* tag = static_cast<BaseTag*>(did->bl);
...


At this point the tag is assigned to the object and I can thus get the object from:


tag->GetObject();


I would prefer to do so in the TagData itself, but the message isn't sent to the TagData.
Is there any method which gets called (or message sent) to indicate the tag is assigned to an object.

I am not using the Execute() method of the TagData, as this gets called more than once during the lifetime of the tag. Should I focus my effort on this method to be triggered with the appropriate object, and keep some state to only handle this once? Or is there a better way to get notified when the object is available to the tag instance?

Thanks.

Back to Top
Andreas Block View Drop Down
Forum Moderator
Forum Moderator
Avatar

Joined: 2014 Oct 01
Location: Hannover
Online Status: Offline
Posts: 1878
Direct Link To This Post Posted: 2017 Nov 14 at 10:30am
Hi,

in general storing information in a tag is perfectly fine and a common use-case of tags.

I'm not so sure about storing the object a tag is assigned to in any way. After all the user can decide to move the tag to other objects for example. So it's probably better to make use of Execute() roughly in the way you describe.

Cheers,
Andreas
SDK Support Engineer
Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 15 at 4:46am
The purpose of obtaining the object the tag is assigned to is in order to initialize tag data which is specific to the object the tag is assigned to (number of points and polygons).
When the tag is moved (or duplicated) onto another object, the tag data needs to be re-initialized with the then applicable number of points and polygons.

Doing this in the tag's Execute() seemed to be a plausible solution, until I read about the fact that this Execute is called in a thread context, and from further reading I understood it was then forbidden to:
- add an event
- call a Draw() function
- display messagebox
- create undos

The main action does happen as a result of user interaction, but the initialization will need to display message box in some cases (unsupported features depending number of points/polygons).

The initialization will also need to trigger redrawing of a UserArea, which is performed by a CallCommand to a separate CommandData plugin's ExecuteSubID(), so probably not a problem since no drawing is actually performed in the Tag::Execute()

I 'll probably also need to create undos since in some cases polygons will need to be selected as a result of the initialization. This needs to be added to some undo, otherwise the polygon remains selected when user undoes the step which triggers the initialization.

Unless I can manage to let the initialization within Tag::Execute() send a (custom) message to the tag itself, which would then perform the whole thing in the main thread (if I understand correctly).

EDIT: Right, I forgot once more that NodeData::Message is synchronous



Edited by C4DS - 2017 Nov 15 at 6:04am
Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 15 at 6:23am
I assumed incorrectly that sending a message was the solution to the threading issue.
Well it can be, but then it needs to be one meant for MessageData::CoreMessage()
as the "Important Threading Information" indicates.
It lists an example that uses the SpecialEventAdd to sent the message, but adding an event was forbidden? Confused!
Back to Top
Andreas Block View Drop Down
Forum Moderator
Forum Moderator
Avatar

Joined: 2014 Oct 01
Location: Hannover
Online Status: Offline
Posts: 1878
Direct Link To This Post Posted: 2017 Nov 17 at 8:07am
There's a fundamental difference between messages (i.e. send and received via Message() in NodeData derived entities) and core messages (send by SpecialEventAdd() and received in CoreMessage() of for example a MessageData).
The first ones are synchronous messages, so the code is executed directly, when Message() is called. If it's called in a threaded context, the receiving side is executing in the same thread context.
Core messages (also event messages) are asynchronous, they get queued and will be received in CoreMessage() in the main thread.

See also the NodeData::Message() manual and the Core Messages manual.

In order to do you initialization work, you may want to look into MSG_MENUPREPARE (also NodeData::Message() - Creation manual), which will be send, when the user creates the tag. When received the tag is already inserted into the object.
And then later on messages like MSG_POINTS_CHANGED or MSG_POLYGONS_CHANGED may help to keep track. These will also be send, if the user moves the tag to another object (even if point/polygon count stay the same).

Cheers,
Andreas
SDK Support Engineer
Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 17 at 8:23am
I already understood the difference between "regular" messages and core messages.
My only confusion was that the documentation mentions that in a thread context no events are allowed to be sent, and then shows to use SpecialEventAdd().

EVMSG_xxx is a core message as a result of an EventAdd().
SpecialEventAdd() also generates a core message, thus an event.
Hence my confusion, of being/not-being allowed to add events from a thread context.

I still need to have a second look at MSG_MENUPREPARE and MSG_POLYGONS_CHANGED, but these will probably be very helpful. Thanks for reminding me of those.

Back to Top
Andreas Block View Drop Down
Forum Moderator
Forum Moderator
Avatar

Joined: 2014 Oct 01
Location: Hannover
Online Status: Offline
Posts: 1878
Direct Link To This Post Posted: 2017 Nov 20 at 8:10am
Hi Daniel,

my apology, I agree, my answer missed the point of your question.

Maybe the warning not to use EventAdd() in a threaded context is a bit harsh. Of course you are right, it just posts an event. The function is thread safe, too. The warning is actually more about, that it should not be done. Posting EventAdd() during scene execution pipeline can easily get you into an endless loop of scene evaluation, for example.
Or maybe view it from the other side: You shouldn't do anything in a threaded context, that makes EventAdd() necessary.

In any case I think, you have a valid point, our documentation is a bit weak here. I have put it on our ToDo list to add some more explanation and context on this topic.

Cheers,
Andreas
SDK Support Engineer
Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 21 at 12:35am
Andreas, no need to apologize.
Your comment about messages did bring up MSG_MENUPREPARE and MSG_POLYGONS_CHANGED, which was exactly what I needed for the initialization part. So, thanks for bringing that up.

However, (yep, there's always a but), I have noticed that MSG_POLYGONS_CHANGED, along with MSG_POINTS_CHANGED, both gets posted twice in a row when the tag is drag-n-dropped from one object onto another. Of course I only check on the changed polygon message, but still process it twice, as I have no clue when to skip the second one.

While it isn't the end of the world to initialize the tag data twice, I am wondering the reason for the double post.
Whenever I modify the object's geometry, I only get the messages once.


Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 21 at 1:51am
Did some further testing with MSG_POLYGONS_CHANGED when I modify an object's geometry.
This message is apparently sent before the whole object structure change is finalized.

Let me explain.
I create a cube primitive (6 polygons) and make it editable.
I then select the top polygon (= polygon index 4)
I switch to edge mode, and select edges via a ring selection, and apply a "connect edges".
This results in 4 of the 6 polygons to be splitted nicely in two. Resulting in 10 polygons, 12 points, no ngons.

However, when the MSG_POLYGONS_CHANGED comes in and following code is executed:

            Int32 polyCount = ToPoly(obj)->GetPolygonCount();
            Int32 pointCount = ToPoly(obj)->GetPointCount();
            Int32 ngonCount = ToPoly(obj)->GetNgonCount();
            BaseSelect* polygonS = ToPoly(obj)->GetPolygonS();
            maxon::BaseArray<Int32> polySel;
            Int32 seg = 0, a, b, i;
            while (polygonS->GetRange(seg++, LIMIT<Int32>::MAX, &a, &b))
            {
                for (i = a; i <= b; ++i)
                {
                    polySel.Append(i);
                }
            }


Results are:
pointCount = 12 -> OK
polyCount = 10 -> OK
ngonCount = 4 -> WHAAAT ???

polySel = Count 1, [4] -> a single polygon with index that of the original polygon BEFORE the geometry change, while that polygon has been splitted into new polygons with indeces 8 and 4.

But the ngon count is what bugs me the most.
Well, OK, the polygon selection is also an important piece of information which I hoped being able to use.
Guess this MSG_POLYGONS_CHANGED isn't the one to rely on after all. It looked such a good solution, though.



Edited by C4DS - 2017 Nov 21 at 2:01am
Back to Top
Andreas Block View Drop Down
Forum Moderator
Forum Moderator
Avatar

Joined: 2014 Oct 01
Location: Hannover
Online Status: Offline
Posts: 1878
Direct Link To This Post Posted: 2017 Nov 21 at 6:03am
No really good news here.

a) When a tag gets dragged to another object, the polygon/point change messages are sent twice. Yes, indeed. There's nothing you can do to avoid this.

b) Also your second observation is correct. MSG_POINTS_CHANGED/MSG_POLYGONS_CHANGED do get sent before the final update of the object (at least in several occasions). So really the only info you should rely upon is the point or polygon count delivered with the message.

Sorry!

Cheers,
Andreas
SDK Support Engineer
Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 23 at 3:03am
No good news indeed.

a) I can do some internal trickery (using the object's GUID) to detect that the second message is for the same object. No worries there.
However, I mistakenly assumed (once more) that when an object's geometry gets changed its GUID remains the same. Apparently this isn't the case.
As such, there is no way to distinguish the incoming MSG_POLYGONS_CHANGED being related to a tag being dropped onto another object, or if the assigned object's geometry gets changed.

Back to the drawing board for me then.

b) for completeness (to those reading this thread):
Along with the message a VariableChanged is passed, containing the old and new point count or polygoncount (depending the message).
In case of moving the tag to another object, the first message passes along in the "old_cnt" the number of points/polygon of the object where the tag is moved away from, and in the "new_cnt" the number of points/polygon of the object the tag is moved to.
The second message then contains the number of points/polygon of the moved-to object in both members.
Not that this is useful to me, but it might for anyone reading this (now or in the future).


Back to Top
Andreas Block View Drop Down
Forum Moderator
Forum Moderator
Avatar

Joined: 2014 Oct 01
Location: Hannover
Online Status: Offline
Posts: 1878
Direct Link To This Post Posted: 2017 Nov 23 at 10:00am
Regarding a) and GUIDs, did you see this thread.
Unfortunately we have no optimal solution in there, either. But at least it contains a lot of background info and maybe some ideas that might help.

Cheers,
Andreas
SDK Support Engineer
Back to Top
C4DS View Drop Down
Member
Member


Joined: 2015 Dec 01
Online Status: Offline
Posts: 235
Direct Link To This Post Posted: 2017 Nov 24 at 1:00am
Hi Andreas,

Yes, I followed that thread when it was active, but didn't care much about the cache and generator related information. I understood the GUID was the way to go for general editing and manipulation of objects.
Read it all again last week, and just now. Still am confused about all that information, as it seems there is not a single way to refer to any object in a unique way.

Furthermore, Riccardo talks about CRC64 being used internally, while only CRC32 is available in the SDK.

I'll have a look if using GUID in combination with GeMarker is a better solution, as the latter seems to remain identical when geometry is changed.

In the end MSG_MENUPREPARE and MSG_POLYGONS_CHANGED seem to be useful, but not in the context I need. As such, I will probably revert to using the scenehook solution, since this was a generic solution when tag is added via tag menu, or InsertTag() or MakeTag() command, or python script.

Back to Top

Forum Jump Forum Permissions View Drop Down

Bulletin Board Software by Web Wiz Forums® version 9.61 [Free Express Edition]
Copyright ©2001-2009 Web Wiz

This page was generated in 0.141 seconds.