I am trying to set up a custom renderer plugin and I am investigating how to catch user’s actions, like add lighting, meshes, delete objects, etc.
I am not sure about the hierarchy of the required classes in order to achieve this.
Which class should have as a member the event watcher to catch these actions?
I have added it in the plugin.h and the eventwatcher indeed works, but I don’t know how trigger the changequeue to apply the changes and give the command to the Display Mode to render the new modified scene.
Is there a Rhino Sample that combines DisplayMode, ChangeQueue and EventWatcher?
So far, I have:
eventwatcher in plunin.h
changequeue in displaymode.h
but I am missing the connection between eventwatcher and displaymode->changequeue.
Any suggestions?
Thanks!
If you are building on ChangeQueue you shouldn’t have to implement an event watcher at all if the goal is to react to document changes: changes to render content, geometry, lights. The ChangeQueue will automatically trigger with NotifyBeginChanges and NotifyEndChanges. Once the latter one is called you can call Flush on your ChangeQueue implementation. This will have the system call all relevant Apply* methods in order if there are changes for those. There is also a NotifyDynamicUpdatesAreAvailable, but in practice there is not much difference between it and NotifyBeginUpdates. When something changes you’ll get either NotifyBeginChanges or NotifyDynamicUpdatesAreAvailable first, followed by NotifyEndChanges. The idea is that you can signal your render engine to get ready for data between these two triggers. You’ll then Flush, handle all data and upload to your engine, after which you can continue.
RhinoCycles is implemented on top of the ChangeQueue. Although it is written in C# the RhinoCommon wrapper around the C++ ChangeQueue code is very thin, so it is pretty much similar in construct.
In RhinoCycles I called my ChangeQueue implementation ChangeDatabase.
Places to look at related to this are:
In the ChangeDatabase note all the Apply* overrides
In my implementation I added a Synchronize method to start the data upload process to Cycles
So to react to light changes you’ll wait for NotifyEndChanges, call Flush on your ChangeQueue implementation. The mechanism will call your implementation of ApplyLightChanges.
This is pretty much what you do also when you first start your engine and what you hopefully already have implementation, barring the NotifyEndChanges. Instead you’d have called CreateWorld, followed by a Flush to have your Apply* methods triggered.
Thank you very much, it was really helpful!
I had some troubles realizing the differences between ChangeQueue and EventWatcher (I thought somehow they had to be combined together). I have some quick questions in order to clear out these concepts and how to use them in my renderer.
Flush: I just added Flush(true) in NotifyEndUpdates() and that made the whole mechanism to catch user’s reactions successfully! Is this the only thing I have to add in order to make change queue work regarding the User-Rhino interaction?
What type of tasks should be carried out by the renderer between NotifyBeginUpdates() and NotifyEndUpdates() ? I have a rendereInit(_scene, _options) in my renderer, but I have inserted it in the DisplayMode just before calling rendering the scene.
Figuring out what ChangeQueue does I am not sure what’s the purpose of EventWatcher. Is it for catching other GUI-related user actions? Is there any sample code for the EventWatcher ?
That entirely depends on your renderer. I have for Raytraced almost nothing other than logging really. You may have seen that for both Notify* calls I check whether the viewport is locked (user has clicked the lock icon in the HUD) and just return if that is the case. The idea is that the user can prevent the viewport with realtime display integration from updating if they choose to do so.
Before the ChangeQueue was introduced you had to write code that included an event watcher. Since that was going to be pretty much the same for all renderer integrations we decided to create a mechanism that does all the reacting to events in the document and handle a lot of the logic that can be quite complex. For instance the ChangeQueue will fully handle geometry, block instances, materials, lights etc. All code that each render engine integration would have to write on their own. This is now centralised in the ChangeQueue. That also means that if there is a bug in code handling changes you can blame us, and we fix it - and everybody building on top of the ChangeQueue benefits from that. Under the hood the ChangeQueue builds on top of the EventWatcher and DisplayConduit. So, when you build on top of the ChangeQueue you don’t have to worry about events, changes to the document and so on.
When you have more questions feel free to ask. Maybe useful to start separate topics for each of your future questions.
I had thought that I had understood how ChangeQueue works when a new mesh is created, but when I tried to investigate what is happening when moving or deleting a mesh I realized that I am a bit far from understanding it. That mainly happens because it’s not clear to me what actually happens under the hood when you type “box” and create a simple box. I would suppose (which is probably wrong) that when you type “box” and create a box in the viewport, two separate things happen:
A mesh with a mesh id is created
An instance with the corresponding xform and mesh id is created.
Then in ApplyMeshInstanceChanges the function receives the list of instances shown in the viewport. Based on this we can get the Mesh Id and the xform of this instance and calculate the actual box that is rendered in the screen. This means that somehow there are two lists, one that shows all available meshes for creating mesh instances and one that provides the list of instances and which mesh id+xform they use.
That was my thought, buy now I see that if I move, scale or delete the box the ApplyMeshChanges is called which means that ChangeQueue works in a different way.
Would it be possible to provide a simple explanation of moving/deleting in ChangeQueue?
Thanks!
FYI, with regular objects (meaning not objects from block instances) the xform is pretty much always the identity matrix, because geometry in Rhino is in world space. The only time it isn’t the identity matrix is during a transform. But once the transform operation has ended the transform is applied to the geometry so it is again in world space with identity matrix.
You’ll see in the ChangeQueue that ApplyDynamicObjectTransforms is called while a selection is being transformed.
You can keep referencing the RhinoCycles implementation for your information on how to use the ChangeQueue. ApplyMeshChanges will be called once the transform has been completed. In my implementation you’ll see that it calls HandleMeshData where for such transform related changes I search from a dictionary for existing mesh id and essentially have the updated geometry swapped in.
You’ll see from the same ApplyMeshChanges that there is a separate list provided for those meshes that are being deleted. The same goes for ApplyMeshInstanceChanges.
I hope that gives you a better understanding of how things work with the ChangeQueue.