JacquesLucke / animation_nodes

Node based visual scripting system designed for motion graphics in Blender.
Other
2.29k stars 342 forks source link

What defines a Calling Operator in an Animation Nodes Python Script SubProgram? #1443

Closed samsenrohm closed 4 years ago

samsenrohm commented 4 years ago

Hello Jacques.

Thanks for your quick response to the T77317, T77323, T77327 issues that I posted at https://developer.blender.org/

You wrote: There is no need to create multiple reports, when you know that they are the same issue. I apologize, after I encountered the issue, and posted the problem, I followed the direction to ensure I was using the the latest version of Blender, and then to troubleshoot the issue further to create the minimum node graph that would make it break. Hence the three issues. And then I could not find a way to delete the first two. So instead I updated the issues to make it clear they were related.

The Blender bug tracker is not the right place for Animation Nodes bug reports. Those should be submitted at https://github.com/JacquesLucke/animation_nodes. Duly noted.

Unfortunately, calling operators in scripts (especially with Auto Execution enabled) is not supported. Question: What defines a 'calling operator'? Is that any function that begins with bpy.ops.? Or?

My objective is to create a plane and a solidified cylinder, and assign them both dynamic paint modifiers of wave and brush respectively, once per animation. Then at different cylinder diameters, for every frame simply move the cylinder up and down, or in and out at different rates (frequencies) to find the combinations that create standing (Faraday) waves, based on the physics that Dynamic Paint provides.

What I was working to accomplish is to ensure that each animation only generates the objects once per animation. The mechanics of Animation Nodes is not clear enough to me, to know how this can be accomplished so that the portion of Node Graph that creates the object is run once. And the portion that moves the solidified cylinder up and down, or scaled in and out is performed once per frame.

The attached .blend file works. Reducing the complexity by attempting to convert nodes to python scripts, did not work (hence this erroneous bug report). It seems I do not understand what part of the node graph is executed on every frame. And whether Animation nodes is intelligent enough to only run the nodes that have inputs that have changed from the previous frame.

Is the mechanics of Animation Nodes explained somewhere so that I could better understand what is taking place under the hood, to ensure that my animations are as efficient as they can be?

My plan is to increase the frame rate to 1000 FPS to model the effect of high frequencies, but have it render out only every 41st frame get to back to 24 FPS so efficiency is important.

CymaNode18.blend.zip

OmarEmaraDev commented 4 years ago

By "calling operator", we mean calling anything that is contextual, that is, depends on the current context. And yes, most operators in bpy.ops fall into this category. It is sometimes possible to get around this through context overwrite, you may read more on this in the following answer.

https://blender.stackexchange.com/questions/6101/poll-failed-context-incorrect-example-bpy-ops-view3d-background-image-add

As for the execution mechanism, Animation Nodes will execute the whole node tree, except for invoke nodes if they are cached. Conditional execution is currently not supported. Animation Nodes doesn't do any implicit caching except for very few cases, where we usually cache IO and constants that are expensive to compute.

I am not entirely sure what you are trying to do, but if you only need to execute a certain graph only once, just do the job of that graph manually. Unless I am missing something, I fail to see why you have to create the meshes, modifiers, and paints manually.

samsenrohm commented 4 years ago

Thank-you Omar, for helping me better understand how Animation Nodes Work. Much appreciated.

You wrote: I am not entirely sure what you are trying to do Using the Dynamic Paint Modifier, I am experimenting in Blender to see if I can get Blender to create standing waves (Faraday Waves) in a circular dish of fluid (water) by oscillating the vertically sided walls of the dish up and down or toward and away from the center of the dish. In the physical world, high speed cameras are used to video small (~ 10mm in diameter) dishes of water or other fluids that are being vibrated in the vertical direction. The results are called Cymatic Images. Here are examples of what some of the results are: https://tiny.cc/FaradayWaves https://tiny.cc/SonaTherapy2011 https://tiny.cc/SonaTherapy2018

The issue is that for a given dish diameter, only certain frequencies will cause a standing wave to form. The phenomena is described here. https://tiny.cc/ChladniPlate

Researchers and artists that generate Faraday Waves to create Cymatic Images have observed that the patterns that form from particular combinations of fluid density, dish diameter, frequency, and amplitude are independent of the depth of the fluid. The same patterns eventually appear whether the fluid depth in the dish is shallow or deep. The only effect of depth appears to have is that the deeper the water, the more time it takes for the standing waves to appear. So I extrapolated that observation, and wondered if a zero depth 2D surface would also produce interesting patterns.

In the physical world, it is difficult to create a multitude of dish or plate sizes, and cumbersome to have fluids or particles of different densities to find combinations that will present patterns for any specific frequency. Instead, experimenters do frequency sweeps at various amplitudes (loudness), scanning for those frequencies for where their specific combination of fluid density and dish diameter being tested produces standing waves. This is much like tuning a radio dial to find the stations that are worth listening to. In between those points is just random noise. My objective is for a given fixed frequency, amplitude, and fluid density; is to vary the dish diameter to find the diameters where standing waves appear.

I understand that Blender Dynamic Paint uses physics calculations, so I thought I would give it a try. So I created a setup in Animation Nodes, where using meshes I can (so far) create and then adjust the

Because I am creating the three objects (Dish, WatersEdge, Water) using animation nodes, what I also need to be able to do is to add modifiers to:

The only way to add these modifiers was to use python scripting. This worked fine.

As for the execution mechanism, Animation Nodes will execute the whole node tree, except for invoke nodes if they are cached. This is why I attempted to put the creation of the size of cylinder and plane inside of a subprogram, so that I could execute it once based on input parameters of:

I thought I could use the ability of Invoke Nodes to cache (run once) to create the objects. Then the Node Tree would only need to move the cylinder on every frame, and not run the regeneration of the two objects. When I put the generation of the Objects into a SubProgram, Blender stopped working as it turns out Instancer Nodes will not run in a subprogram. Since I was able to add modifiers using Python scripting, I thought I could also create the objects themselves using Python to reproduce what Instancer nodes can do outside of a subprogram. Since I could add modifiers inside or outside a subprogram using python scripts, I assumed that including the creation of objects inside of these same scripts (that consistently crash Blender) was a bug.

In the parameters for a Node Tree, enabling [Auto Execution] allows the Node Tree to be executed on every [Frame Changed]. In my animation, if [Frame Changed] is unchecked the walls move once at the beginning of the animation, creating a single pulse that is then evaluated by Dynamic Paint for the remainder of the animation. If [Frame Changed] is checked on, then the walls move based on the values of [fltAmplitudePercent] and [fltFramesPerCycle] to create a vibration that results in a pattern.

In my working NodeTree (see attached) if the plane was being recreated on every frame, then I would expect that the waves captured by dynamic paint would be wiped out every frame, but it does not seem to work that way. However I do notice that when I create the plane manually (outside the animation/node tree) the animation is faster, as it is not recalculating the whole tree. Hence I am unclear about how Animation Nodes recalculates the whole tree, that includes creating the plane, yet the animation shows that the plane 'remembers' the waves it had from the previous frame? CymaNode18b blend

If the node tree was fully being executed on every frame, and the node tree is creating the objects, then I don't understand how Dynamic Paint can display the Physics on an object that is being created on every pass through the Node Tree.

Conditional execution is currently not supported. In Omar's answer to a different question: https://blender.stackexchange.com/questions/132753/skipping-an-item-inside-a-loop-in-animation-nodes

I am not sure if this could be applied to execute a portion of a node on Frame 1, and then not execute it for the remainder of the animation?

if you only need to execute a certain graph only once, just do the job of that graph manually. Unless I am missing something, I fail to see why you have to create the meshes, modifiers, and paints (in the Animation Node Tree) manually.

In Omar's answer to a different question here: https://blender.stackexchange.com/questions/136610/animation-nodes-copy-a-selection-group-of-objects He wrote: "Instance all the objects you want, possibly using the Copy All Option or by disabling the Copy From Source option and injecting the object data later." (Not sure how this is different that what is being done in my animation node. Again I am able to create the objects, what I don't seem to be able to do, is execute the creation once, and not have AN try to create them on any frame... if it is doing so at all.)

  1. The Solidify Modifier can only be applied using a Python Script (per Omar's instruction) https://blender.stackexchange.com/questions/93938/is-it-possible-to-add-modifiers-dynamically-with-animation-nodes

  2. Once Solidify is applied, changing the wall-thickness is not easy to do, it is most straightforward to recreate the cylinder and then apply Solidify with the new thickness.

CymaNode18.blend.zip

OmarEmaraDev commented 4 years ago

@samsenrohm It appears Blender keeps the simulation data in a special physics data block, not in the mesh itself, so overwriting the mesh with an identical copy will not make a difference. That's probably why the simulation continue and doesn't reset. As for your observation that the node tree is significantly faster once you create the grid manually. That's because most of the time is spent in setting the mesh data, as can be seen when inspecting the execution time of the nodes. (25ms of the 26ms execution time is spent setting the mesh, so removing this node would reduce the execution time to just 1ms)

20200605-175329

So a simple solution would be to either cache those nodes like you did, or simply disable the mesh setting while doing the simulation, manually by pressing the small circle in the Mesh input.

There are probably some "silly" workarounds to do this automatically, but if it is very important to you, I would just take the easy solution.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.