Redraw GeUserArea during object movement
On 21/10/2015 at 05:10, xxxxxxxx wrote:
Cinema 4D Version: R16
Platform: Windows ;
Language(s) : C++ ;
I have a custom GUI control implemented using GeUserArea, where I draw some info based on properties of active object (including its matrix). Now, I can redraw user area whenever the object is updated (by listening to CHANGE message), but I cannot find any way to do updates while user is moving with active object in viewport.
The problem is with the GeUserArea::Redraw method - when I call Redraw from another thread with threaded=true, it won't redraw the area till the user stops moving with object (I guess the redraw request is added to some queue and is not done till C4D stops with event polling in main thread). If I want to redraw immediatelly, I have to call Redraw(false), but I can only do that from main thread which is blocked by polling of mouse events by C4D.
Currently, I use MSG_DESCRIPTION_GETINLINEOBJECT to redraw user area (this message is called from main thread during movement), but it is very suboptimal solution (and quite probably dangerous one).
So to summary this, is there some way to either:
- immediatelly redraw user area from different thread
- have my code executed regularly while C4D is polling mouse events during user interaction with active object?
On 26/10/2015 at 02:40, xxxxxxxx wrote:
I guess you are using a GeDialog? You can trigger the user area redraw when you catch the core message EVMSG_ASYNCEDITORMOVE.
On 28/10/2015 at 16:38, xxxxxxxx wrote:
thanks, EVMSG_ASYNCEDITORMOVE helps (I don't know how I missed that one), but unfortunatelly, I forgot to add one important detail in my previous question - before drawing to user area, I need to do some (quite heavy) precomputations. Obviously, I cannot do those in GeUserArea::DrawMsg, so I do this in separate thread and in DrawMsg, I only use already computed data. Now, when I call Redraw during EVMSG_ASYNCEDITORMOVE, it can use only data calculated from previous object matrix (as thread doing precomputations just started recalculating with the new matrix). This isn't much of a problem with continuous moving with object, but when user quickly moves with object and then stop moving (without releasing mouse button), I have no way to redraw area...
So, what I really nead is calling redraw between MOVE_START and MOVE_END even when there is no MOVE_CONTINUE being sent...
On 29/10/2015 at 01:53, xxxxxxxx wrote:
a GUI update (and the redraw of a user area) should only be triggered from the main thread. If the user keeps the mouse pressed the main thread is typically stopped so there is no way to safely trigger the redraw.
On 29/10/2015 at 03:19, xxxxxxxx wrote:
I do the Redraw(true) which works fine for GeUserArea drawing from threads.
about mouse movement, you can check if the mouse button is held and not moving "to take decisions about drawing".
On 29/10/2015 at 04:41, xxxxxxxx wrote:
Ok, I was hoping in some king of message called periodically during mouse polling... I will try some alternative approaches, but most of the remaining possibilities are quite unsafe (e. g. windows hooks).
Yes, Redraw(true) works from threads, but (at least for me) it does not redraw immediatelly. Instead, it seems to only schedule the redraw internally in C4D (as Sebastian said, it can be done only from main thread). In my case (user moving with object), this results in actual redraw being done once user release mouse button (ie once C4D stops polling mouse messages and main thread starts working normally).
On 29/10/2015 at 05:54, xxxxxxxx wrote:
you can draw while mouse is dragging
there is asynctest.cpp example in the SDK, it does exactly this "draw to a BaseBitmap, then Redraw when mouse event is dragging"
On 29/10/2015 at 09:40, xxxxxxxx wrote:
Yes, but this is unfortunatelly slightly different - in asynctest.cpp, the user area handles the messages in InputEvent and has all control over how everything is processed (polling loop is executed there). In my case, everything is done in scene viewport, so C4D handles message polling and it only calls messages like EVMSG_ASYNCEDITORMOVE while doing this. I can catch some mouse-related events in SceneHook, but no mouse moving and nothing that would allow me draw when mouse is not moving.
On 29/10/2015 at 09:52, xxxxxxxx wrote:
if I understand correctly, there is:
virtual Bool SceneHook::AddToExecution(BaseSceneHook* node, PriorityList* list);
On 29/10/2015 at 10:31, xxxxxxxx wrote:
Yes, but Execute() is then called in separate thread:
virtual [EXECUTIONRESULT](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/ge_prepass/enum_EXECUTIONRESULT528.html) Execute([BaseSceneHook](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/c4d_basedocument/class_BaseSceneHook77.html)* node, [BaseDocument](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/c4d_basedocument/class_BaseDocument64.html)* doc, [BaseThread](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/c4d_thread/class_BaseThread85.html)* bt, LONG priority, [EXECUTIONFLAGS](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/ge_prepass/enum_EXECUTIONFLAGS526.html) flags)_<_h4_>_
Called at the point in the priority pipeline specified by [AddToExecution()](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/c4d_scenehookdata/class_SceneHookData1319.html#addtoexecution7), or by [_L">RegisterSceneHookPlugin()](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/c4d_scenehookdata/global_c4d_scenehook224.html#registerscenehookplugin0).
Important: This function is called in a thread context. Please see the [important information](file:///C:/Users/Ale%C5%A1/Downloads/C4DR14034SDKHTML20121221/help/pages/general_docs/doc_Threading1463.html) about threading.
So I can only use Redraw(true), which will be blocked by C4D untill moving finishes... Also, I would be in similar situation as with EVMSG_ASYNCEDITORMOVE...
On 29/10/2015 at 17:33, xxxxxxxx wrote:
here is a quote from the function help description:
"By default this function returns FALSE. Then C4D will call Execute() at the priority specified in the _<_span title="bool c4d_scenehook::registerscenehookpluginlOng id, const &str, lOng info, allocator g, lOng priority, lOng disklevel, void emulation=null"_>_RegisterSceneHookPlugin() function.
If you override this function and return TRUE, then you can insert your own points of execution in the list by calling for example:
_list_ ->[Add(tag, EXECUTION_ANIMATION, 0)](); _list_ ->[Add(tag, EXECUTION_GENERATOR, 0)]();
CINEMA 4D will then call Execute() two times."
so it should call Execute at the point you specify, in that case:
list->Add(node, EXECUTIONFLAGS_INDRAG, 0);
list->Add(node, EXECUTIONFLAGS_INMOVE, 0);
this should work I guess, also I can see examples that are already doing a SceneHook during object drag "like Arnold IPR"
On 06/11/2015 at 06:11, xxxxxxxx wrote:
@Firielentul , you were correct, I tested what I wrote here and it didn't work.
but there is a solution:
you want to draw one more time after MOVE_CONTINUE finishes, this can be done with 2 flags.
if id == MOVE_CONTINUE: current_flag = a else current_flag = b if prev_flag == a and current_flag == b: Redraw() prev_flag = current_flag