Different behavior between editor and render
-
On 06/11/2015 at 10:46, xxxxxxxx wrote:
User Information:
Cinema 4D Version:
Platform:
Language(s) :---------
Hi,Is it normal behavior that my plug-in (for exemple making noise on position or set visibility) work nice in editor and as expected but when I press render it's different story.
Frame 0 of render showing my objects who should be off !
Noise work on position but not on rotation and scale but editor everything move correctly!
Time re-sampling work in editor and not in render (frame - frame % step ) !Thanks to any guys who help me
-
On 06/11/2015 at 12:56, xxxxxxxx wrote:
That depends on how your plugin code handles its data, for example. Of course without code, it's just guesswork, but here's something to keep in mind:
- When a render starts, the rendered scene is a copy of the active scene (or whatever you load into the renderer). If a plugin doesn't support copying properly, then the result in the render scene may be garbage.
- BaseTime may yield results that the editor doesn't. For example, if your render has full scene motion blur, then the time will be occasionally between frames (check the numerator/denominator values for that). The frame value however may show only the full frame that is calculated!
I don't know how you apply your noise so I wouldn't know about that, though.
Maybe show some code?
-
On 06/11/2015 at 13:15, xxxxxxxx wrote:
Thanks for this quick and nightly answer !! Of course without code it's not easy to answer to this. But in fact this is a more larger question (and your response is pretty interesting).
The good question could be : what it the good practice for preparing plug-in for render ?
And as you told me : whats is good practice to copy parameters ?
Finally thanks again
-
On 06/11/2015 at 13:18, xxxxxxxx wrote:
For more information I use a struc to store the values from the description of my tag instantiated in init
-
On 06/11/2015 at 14:42, xxxxxxxx wrote:
Difficult to say. I have programmed plugins that work fine without any other precautions. If you store your parameters inside of the BaseContainer, you should be fine.
Recently though I did one that didn't want to render. In that case, the reason is that it contains pointers to allocated arrays (which are not copied). The solution, which I already applied to one earlier plugin, is that I wrap my own data in a C4D Datatype and store that in the BaseContainer. This Datatype provides copy and save/load functionality (so the plugin automatically saves its data, too - very convenient).
If this struct is the issue here, you may want to try the same - write a Datatype, equip it with the necessary functionality, and fill it with your struct data. Then put an instance of that Datatype into your BaseContainer.
(I'm guessing though - to find your error, you may need to debug.)
-
On 07/11/2015 at 02:59, xxxxxxxx wrote:
To make sure your custom data is fully preserved (for rendering but also in general) you need to make sure the CopyTo() function is overloaded. This is called when the BaseDocument is for example copied for rendering (then you need to copy any custom data of yours as well to that new copy).
You can also store it in the object's (or tag's etc..) basecontainer as mentioned by Cairyn as it is copied over by C4D automatically.
Without code it's impossible to say what the reason is.
-
On 08/11/2015 at 03:31, xxxxxxxx wrote:
Thanks for all your answers.
I took care about what you said. So now I retrieve all my data inside EXECUTE from my tag. I do that with tag->GetData().GetWhatEver(ID); Like I could see inside some examples of the SDK.
It's a bit more slow but acceptable (about 0.002 millis more). I really care about execution duration because my plug-in is very tiny and should work with a gorgeous number of objects. So this is the point :
I get a string from a form inside my Description tag and apply multiples Reg ex on it. It's not slow. But too slow to be executed on each frames.
So i compute it in advance, at tag initialization, when use change values, when user change minTime max time and FPS (with this MSG 1073741828).
What is the best way to store values who is not in interface inside tag data ? This is a std::vector<bool> were i store if my object is visible or not and. And the index to retrieve it is the current frame, it's very fast !
Should I use tag->InsData() in my init procedure to add this std::vector to my data instance ?
Or should I leave this as is and simply copy this vector from source to destination inside my CopyTo function ? But if I do that I'll have to recreate all normal behaviors for the CopyTo and i'm not sure to know how to do that.by the way, thanks guys for your time
-
On 08/11/2015 at 04:26, xxxxxxxx wrote:
I wrote that :
Bool timer_visibility::CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn){ TagData::CopyTo(dest, snode, dnode, flags, trn); ((timer_visibility* )dest)->computeFrame = this->computeFrame; return true; }
I'll try when i could compile due multiples error on others things.
-
On 08/11/2015 at 05:38, xxxxxxxx wrote:
Looks okay to me at first glance.
Note that if "computeFrame" is a pointer to dynamically allocated data, you need to copy that data too (allocating new heap space for the copy).
Also, you should check the return value of the parent call and return that, not just discard that value.
-
On 08/11/2015 at 05:54, xxxxxxxx wrote:
One more question about getting information.
For example this code :
Bool timer_visibility::CheckDescriptionValues(GeListNode *node){ GePrint( String::IntToString( ((BaseDocument* )node)->GetFps() ) ); GePrint( String::IntToString( ((BaseDocument* )node)->GetMinTime().GetFrame( ((BaseDocument* )node)->GetFps() ) ) ); return true; }
Called from :
Bool timer_visibility::Message(GeListNode * node, Int32 type, void *data){ (...) case MSG_DESCRIPTION_CHECKUPDATE: CheckDescriptionValues(node); break; (...) }
Why this don't send me the right FPS ? I got 1002 instead... And it's the same for this GetMinTime and GetMaxTime sends me 0
Thanks for your reply ! this help me !
-
On 08/11/2015 at 06:41, xxxxxxxx wrote:
Your node in the parameter list doesn't point to the BaseDocument, but to your tag. You are casting to BaseDocument* without even checking whether the node is actually of the fitting class...
Use
node->GetDocument()
to retrieve the proper BaseDocument* that you can then ask for the time.
Also, check the return value for nullptr, because your node may not be in a document at all. -
On 08/11/2015 at 08:52, xxxxxxxx wrote:
Perfect ! I did not realize that my node was not derived from the document...
I'm trying in in my initialization sequence to get document from node like you show me :
Bool timer_visibility::Init(GeListNode* node) { // Setup des éléments d'affichage BaseDocument* doc = node->GetDocument(); if (!doc || doc == NULL || doc == nullptr) return false; GePrint(String::IntToString(doc->GetFps())); ResetDescriptionValues(node); CheckDescriptionValues(node); return true; }
But now I can't init my plug-in. I think it's because i can't catch Document in my init sequence ?
I would like to do that to initialize in frame and outframe of my tag with doc->minTime and doc->maxTimeMaybe I have to add flag boolean value and if it's first EXECUTE do something and after reset my flag ? Or it's possible to do that ?
-
On 08/11/2015 at 09:45, xxxxxxxx wrote:
When a node is created (and system Init is called) the node is not yet part of the document so you don't have a connection there.
If you have some data that depends on the timeslider, and you don't want to access/rebuild it on the fly every time, then you need to consider a few more situations:
1. The node is cut and pasted (temporarily losing the document connection)
2. The node is saved and loaded
3. The node is copied to a different document with other values in the timesliderand so on. Your idea of having a boolean marker to check whether your data is built already is basically right, but it does not suffice to cover all cases.
Now, when confronted with this situation, a software developer usually approaches it like this:
Write a function for accessing your "cached data". This function will hide the full complexity of the cache from the caller. It will internally store (in the node) the conditions under which the cache is valid. And it may return null if it fails to access or rebuild the cache, which the caller needs to take into account.
In pseudocode:
GetData (<parameters that are needed to build the cached data>) { Boolean rebuildCache = false; <find the current conditions from the parameters>; <if that doesn't work, return null>; if <cache is built already> { if <original conditions are different from the current ones> { rebuildCache = true; } } else { rebuildCache = true; } if (rebuildCache) { <throw away old cache, if there is any>; <do whatever is needed to build your data from the current conditions>; <if that doesn't work, return null>; } return <cached data>; }
The conditions and the data cache are permanently stored in your tag.
Now, this method does not require any initialization because it checks by itself whether an init is needed. Your start state would be "no data is present" which would force the method to rebuild the cache when it is accessed first.
On every following call, it will determine automatically whether a rebuild is needed. For example, if the cache depends on the FPS of the document, it would look at its current document and get the FPS from there, and then compare it with the FPS that was used lastly to build the cache. If that's the same, then it can use the old cache. If it's different, then the old cache is invalid (because it has been built under different conditions) and needs to be recreated with the new FPS.
The caller must just be aware that there are situations when the cached data cannot be made available (e.g. when the node is not part of a document...), so it must fail gracefully (not causing null pointer exceptions or something).
The nice thing here is that you do not need to listen to messages that may force you to change the cache (and always be in danger that you miss something). You don't need to know when the user changes the minFrame and maxFrame, because the cache access will check these conditions dynamically, and rebuild the cache when needed.
-
On 10/11/2015 at 03:32, xxxxxxxx wrote:
oops, forgot one important step: after building your cache you need to store the conditions under which the cache was created:
(...) <do whatever is needed to build your data from the current conditions>; <if that doesn't work, return null>; <store current conditions as "original conditions" for the next call>; (...)
Otherwise, you wouldn't have these conditions available to check against new conditions in future loops.
-
On 10/11/2015 at 05:06, xxxxxxxx wrote:
Thanks for your documented reply. It's perfect behavior for dynamics and complex structures. I'll make mine soon.
My problem was not so much complicated and does not require this. Because i just need to execute one tiny part of code one time at the first use. So i use the flag idea ! But don't worry your code will help me on the next problem and it's better than mine.
The flag code is here :
struct myPlug_data{ Bool flag_thisThingDone; Float any other data you need to store 1; Float any other data you need to store ...; }; class myPlug : public TagData{ (...) public: virtual Bool Init(GeListNode* node); virtual Bool Message(GeListNode * node, Int32 type, void *data); virtual Bool CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn); Bool GenerateCache(GeListNode* node); (...) private: myPlug_data Data; }; Bool myPlug::Init(GeListNode* node){ Data.flag_thisThingDone = true; (....) } Bool myPlug::Message(GeListNode * node, Int32 type, void *data){ switch (type){ case MSG_DOCUMENTINFO: GenerateCache(node); break; case MSG_DESCRIPTION_CHECKUPDATE: GenerateCache(node); break; case 1073741828: GenerateCache(node); break; } Bool myPlug::CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn){ TagData::CopyTo(dest, snode, dnode, flags, trn); ((myPlug* )dest)->Data = this->Data; } Bool myPlug::GenerateCache(GeListNode* node){ if(Data.flag_thisThingDone){ do your business one time. Data.flag_thisThingDone = false; } do your business all the time. return true; }