Forcing Python node to recook when its dependencies change

   3018   12   2
User Avatar
Member
131 posts
Joined: Sept. 2021
Offline
Hi!

Inside of an HDA, I have a Python node that gets the world transformation of the camera object that is specified in a path in the HDA.

This is the code in the Python node:
node = hou.pwd()
geo = node.geometry()

camera_path = node.parent().parm("camera_path").eval()
camera = hou.node(camera_path)

transforms = camera.worldTransform()

rotation = transforms.extractRotationMatrix3()
location = transforms.extractTranslates()

print("Camera rotation", rotation)

When I select the Python node and hit Ctrl + Enter, the rotation matrix of the camera is properly printed to the Python shell.



However, if I rotate the camera in world space, this Python node does not seem to recook. It doesn't print out the new rotation matrix of the camera unless I jump into the node, change the code (e.g., add a new line) and then hit Ctrl + Enter again.

How can I make it so that this Python node recooks every time the things it depends on (like the Camera) change?

Thanks!
Anson
Edited by AnsonSavage - May 20, 2023 23:31:27

Attachments:
matrix.png (7.5 KB)

User Avatar
Member
1922 posts
Joined: Nov. 2006
Offline
So this should work (and does in a quick test scene I made) so you might have to provide an example file to help see why it's not recooking on changes.

Without seeing the scene, my only real thought is that whatever node chain your HDA is displaying isn't involved with the Python SOP so it has no reason to recook.
Graham Thompson, Technical Artist @ Rockstar Games
User Avatar
Member
274 posts
Joined: July 2013
Offline
The reason this doesn't work is because there is no update event that triggers an update for the python node. Python only reads the cam values when eval'ed and that's it.

When referencing a channel in vex/expressions an update of the channel does trigger an update of the vex/expression code.

btw, a very dirty way to trigger a python update is to include a channel reference in a python comment line

#dummy comment `ch("/obj/someCam/tx)`to trigger update

With the above example changing the cam's x-position will cause the python node to update. needless to say, there are more elegant ways to do this
More code, less clicks.
User Avatar
Member
15 posts
Joined: Dec. 2021
Offline
graham
So this should work (and does in a quick test scene I made) so you might have to provide an example file to help see why it's not recooking on changes.

Without seeing the scene, my only real thought is that whatever node chain your HDA is displaying isn't involved with the Python SOP so it has no reason to recook.

Hi! Here's my HIP file! The main reason I'm using Python is because I want to get the global orientation of the Camera, after parenting and constraints and everything like that. If there's a way to do this with Vex, even better.

Image Not Found

Attachments:
focal_lock_in_houdini.hipnc (241.9 KB)

User Avatar
Member
15 posts
Joined: Dec. 2021
Offline
Jonathan de Blok
The reason this doesn't work is because there is no update event that triggers an update for the python node. Python only reads the cam values when eval'ed and that's it.

When referencing a channel in vex/expressions an update of the channel does trigger an update of the vex/expression code.

btw, a very dirty way to trigger a python update is to include a channel reference in a python comment line

#dummy comment `ch("/obj/someCam/tx)`to trigger update

With the above example changing the cam's x-position will cause the python node to update. needless to say, there are more elegant ways to do this

Okay, neat! For some reason, that didn't seem to force the recook (at least it's not printing the updated values as the camera changes position).

Thanks!
User Avatar
Member
1922 posts
Joined: Nov. 2006
Offline
AnsonSavageBYU
graham
So this should work (and does in a quick test scene I made) so you might have to provide an example file to help see why it's not recooking on changes.

Without seeing the scene, my only real thought is that whatever node chain your HDA is displaying isn't involved with the Python SOP so it has no reason to recook.

Hi! Here's my HIP file! The main reason I'm using Python is because I want to get the global orientation of the Camera, after parenting and constraints and everything like that. If there's a way to do this with Vex, even better.

Image Not Found
Thanks for the hip file! From looking at it, you're running into essentially an infinitely recursive situation, though Houdini is silently not doing anything or warning you about it...

Because you're evaluating the focal parm in your Python SOP and then down the chain trying to set that parm again, it's causing a bunch of issues. At least in the hip file posted you're not actually doing anything with that "current" value anywhere, just printing for I'm guessing debug purposes? If you get rid of that (in the get data node and compute nodes) the problem mostly solves itself. That said, using that other Python SOP to set a parameter value during cook is also a very bad idea. A more proper solution would be to have the focal parameter on the camera just use a detail() expression to grab the computed value from the compute node.

You should also be able to easily grab the transform matrix in VEX without doing any Python at all using optransform() to get the matrix and then getting the position and rotation matrix from that instead of relying on the Python node to provide it.
Edited by graham - May 22, 2023 20:06:22
Graham Thompson, Technical Artist @ Rockstar Games
User Avatar
Member
15 posts
Joined: Dec. 2021
Offline
graham
Thanks for the hip file! From looking at it, you're running into essentially an infinitely recursive situation, though Houdini is silently not doing anything or warning you about it...

Because you're evaluating the focal parm in your Python SOP and then down the chain trying to set that parm again, it's causing a bunch of issues. At least in the hip file posted you're not actually doing anything with that "current" value anywhere, just printing for I'm guessing debug purposes? If you get rid of that (in the get data node and compute nodes) the problem mostly solves itself. That said, using that other Python SOP to set a parameter value during cook is also a very bad idea. A more proper solution would be to have the focal parameter on the camera just use a detail() expression to grab the computed value from the compute node.

You should also be able to easily grab the transform matrix in VEX without doing any Python at all using optransform() to get the matrix and then getting the position and rotation matrix from that instead of relying on the Python node to provide it.

Ah, that makes sense! So maybe I can have some Python code that puts the detail() expression into the camera when the node is enabled. I was hoping to make it able to push values (instead of requiring the camera to pull values), but I see what you're saying now, I guess pushing values would make it change and this would call the first Python node again creating an infinite loop.

Thanks for the hints!
Anson
User Avatar
Member
274 posts
Joined: July 2013
Offline
If I take the hip file you posted and add this line the the "set_new_focal_lenght" node it works fine and prints updates.

#dummy comment to trigger update `optransform( chs("../camera_path") )`

But again, this is solution is not going to win any prizes and as Graham suggest doing this though attributes is better way.




And btw.. a much overlooked feature in the wrangle/expression nodes is in the wrangle's src edit window context menu->reference->scene data' where you can generate vex to get you started on retrieving all sorts of scene data:


Edited by Jonathan de Blok - May 23, 2023 04:24:28

Attachments:
Screenshot 2023-05-23 101054.jpg (399.8 KB)
Screenshot 2023-05-23 102303.jpg (148.5 KB)

More code, less clicks.
User Avatar
Member
15 posts
Joined: Dec. 2021
Offline
Jonathan de Blok
If I take the hip file you posted and add this line the the "set_new_focal_lenght" node it works fine and prints updates.

#dummy comment to trigger update `optransform( chs("../camera_path") )`

But again, this is solution is not going to win any prizes and as Graham suggest doing this though attributes is better way.

Image Not Found



And btw.. a much overlooked feature in the wrangle/expression nodes is in the wrangle's src edit window context menu->reference->scene data' where you can generate vex to get you started on retrieving all sorts of scene data:


Image Not Found
Hey, I love this info, this is super helpful!

I'm experiencing two issues: if I have the camera reference the new focal length in a detail attribute, this creates infinite recursion (I guess when the detail attribute changes, the camera changes, which causes the Python node to cook again which recooks the node the computes the new focal length...)

Also, thanks for the tip on using the scene reference data! Looking at the code that makes it up it appears that it still references the entire camera SOP and would thus recook the node even if only the focal length changes. Any tips on how to avoid this?

Thanks!
Anson
User Avatar
Member
15 posts
Joined: Dec. 2021
Offline
Jonathan de Blok
If I take the hip file you posted and add this line the the "set_new_focal_lenght" node it works fine and prints updates.

#dummy comment to trigger update `optransform( chs("../camera_path") )`

Also, I added this line and it does exactly what I need it to do! Very interesting.
User Avatar
Member
131 posts
Joined: Sept. 2021
Offline
I guess the main reason this confuses me is: why does this solution (of putting a reference to the camera's path in a comment of the Python node) eliminate the issue of infinite recursion (which apparently was the reason it wasn't cooking originally)?
User Avatar
Member
274 posts
Joined: July 2013
Offline
that's because only a transform matrix change will trigger an update (that's what we're inserting into the comment line). A focal change has no effect on that matrix and hence there is no looping issue.
More code, less clicks.
User Avatar
Member
1922 posts
Joined: Nov. 2006
Offline
AnsonSavageBYU
I'm experiencing two issues: if I have the camera reference the new focal length in a detail attribute, this creates infinite recursion (I guess when the detail attribute changes, the camera changes, which causes the Python node to cook again which recooks the node the computes the new focal length...)
Not sure how you've got it set up, but it seems to work for me okay. See attached file where I've put it all in the compute wrangle and am using the detail() expression pointing to it without issues.

Also you should be able to fold all the 2nd input stuff into the same wrangle as it's not doing anything terribly complex.

Attachments:
focal_lock_in_houdini_graham.hipnc (199.7 KB)

Graham Thompson, Technical Artist @ Rockstar Games
  • Quick Links