Jump to content

CmiVFX Python Rubiks Cube


Nerox

Recommended Posts

Hi!

I'm following the CmiVFX video: 'Houdini Empowering Digital Assets With Python'. I'm new to python, (started with the 'CmiVFX - Intro to Python' video) during the class I run in to a problem and I just can't figure out how to fix it.

Using the python script I set keyframes, however once the first key frame is set the parameter values turn to zero and aren't changeable any more. There is a line in the code which should take care of this:

parm_tuple.lock((False,False,False))

If I print this, it returns: 'None'

I've tried to find help cards for lock(), but I guess I don't understand the syntax jet, because I would say that lock() would be a submodule of hou.node and help(.hou.node.lock) should give a help card right? I'm running 10.0.554. Hope you can help me :-).

The function which set the keyframe:

def setkey(rubiksInstance):

- for i in range(1,27):

-- #declare path to current rotation value

-- parm_tuple = rubiksInstance.parmTuple('small_cube_'+str(i)+'_r')

-- #set parm scope

-- parm_tuple.setAutoscope((True, True, True))

-- #unlock channel

-- parm_tuple.lock((False,False,False))

-- result = parm_tuple.lock((False,False,False))

-- print parm_tuple

-- print result

-- #retrieve current rotation values

-- rot = parm_tuple.eval()

-- #set keyframe variable X

-- rot_keyframe = hou.Keyframe()

-- #No idea what happens here...

-- rot_keyframe.setValue(rot[0])

-- #change curve interpolation

-- rot_keyframe.setExpression('qlinear', hou.exprLanguage.Hscript)

-- #set key

-- parm_tuple[0].setKeyframe(rot_keyframe)

CMIVFX_DA-Python.zip

Edited by Nerox
Link to comment
Share on other sites

Your problem is that you are setting an invalid expression for the keyframes. You are seting "qlinear" where you should be setting "qlinear()". This is why you always get values keyed as 0.

Unless there are more things coming in this video I don't understand why you bother unlocking the parameters at all since you never seem to lock them.

Also, the lock() method is a member of the hou.Parm and hou.ParmTuple class. Hence parm_tuple.lock(). help(hou.ParmTuple.lock)

Edit: I wish you luck on this endeavor. Just looking at this little bit of the project it looks like quite the gong show. Not sure who the guy who did this video is but I can't say I'm impressed with his methodology or coding.

Edited by graham
Link to comment
Share on other sites

Your problem is that you are setting an invalid expression for the keyframes. You are seting "qlinear" where you should be setting "qlinear()". This is why you always get values keyed as 0.

Unless there are more things coming in this video I don't understand why you bother unlocking the parameters at all since you never seem to lock them.

Also, the lock() method is a member of the hou.Parm and hou.ParmTuple class. Hence parm_tuple.lock(). help(hou.ParmTuple.lock)

Yes! That's it :-).

Edit: I wish you luck on this endeavor. Just looking at this little bit of the project it looks like quite the gong show. Not sure who the guy who did this video is but I can't say I'm impressed with his methodology or coding.

Like I mentioned before I'm totally new to python, so I can't agree or disagree with you based on experience. Luckely I've only 8 more minutes of video to go and in my dullness it has been very useful. How ever there have been some moments where I thought: 'there must be a better way to do this'.

Thanks for your help Graham, I appreciate it.

Nick

Link to comment
Share on other sites

Guest Swann
How ever there have been some moments where I thought: 'there must be a better way to do this'.

So there are three of us.

To be fair every CmiVFX tutorial about coding is not on levels I wanted to be. This is the part that just sucks in Cmi, even more than their distribution system.

For this price definetely better solution is to buy 3DBuzz xna101 course that will give you big kickstart into programming. There are 120 hours of material and most of first and second disk is theory that is covered really deep (second disk is almost 60 hours long and 50 of it is theory). Before those course I didn't understand (even if it was explained) what is __init__ etc. It just looked for me like magic. With knowledgo from XNA course now I know what is constuctor on examples and is clear for me how and why to use it. XNA is not for Python but this course gives you all those things that you should know if you wan't to understand programming in any modern language (Polymorphism, Inheritance etc). After this you will not have any problems with reading code. You will propably look at VEX and see how much simpler verion of C it is. You will still propably not have skills to write complicated programs, but hey, practice make masters and it's easier to practice with teory working in your head.

EDIT: What I really like to see is DOPS course simmilary long and deep and to 3DBuzz XNA or ADP class (ADP is 160 hours and it's stil going :)) Yep, weakup Swann, this will never happen.

Edited by SWANN
Link to comment
Share on other sites

I've stated my feelings on the cmiVFX method of distribution before but I generally don't mention the quality of there videos. Some of them I have seen have been alright. Some not so much. In general, I find the higher quality Houdini videos are usually done by someone with a name I recognize as being active in the Houdini community. Videos that seem to be a miss are usually done by people who usually have 3d experience, but not so much as a solid Houdini user: They basically try and learn the package or content they are showing just for the purpose of the video. This isn't exactly cmiVFX specific but I feel they follow the trend.

Now I haven't seem this particular video and know nothing about the author other than what I just read on the website, but based on the bit of output, and with no disrespect, I'd say that while the general concept of the rubik's cube is good, the underlying implementation is mediocre. It seems to work but it could be a lot better. I'd love to be able to watch the video and offer a full array of comments on it but I'm not going to pay $50 to see something done I've been doing for years. Also, for a video discussing empowering your assets with Python it sure leaves out a lot of the other ways you can make your assets better with Python; no event handlers, interesting callbacks, menu parameters, tool scripts, etc.

I was actually mildly interested in checking out the cities based off gps data but most likely I'll end up passing.

My biggest issue with the code that I saw inside this asset was the sheer amount of redundancy and incorrect practices. There's also a lot of clutter. Without having seen the completed asset I can't fully comment but there just seemed to be a bunch of stuff that was useless or could have very easily been done better better, with less code and been more readable.

I took the bit of code in the posted asset, ripped like 40 or 50 lines out just by replacing them with better ways to do some stuff, as well as added some more stuff to make it much more readable. If someone has a completed version of the asset they wouldn't mind sharing I'd love to give it a bit of a rewrite to show people different ways to do things.

Edit: Also, I should mention that you can really learn a fair amount of things by reading the bits and pieces in the help docs, as well as the HOM cookbook. Most of the common things that are done are discussed in there and there's some pretty good examples. If you are interested in object level transforms with matrices and such I also recommend taking a look at the help for hou.ObjNode.setWorldTransform. It's got a good beginner explanation of dealing with transform matrices.

Edited by graham
  • Like 1
Link to comment
Share on other sites

EDIT: What I really like to see is DOPS course simmilary long and deep and to 3DBuzz XNA or ADP class (ADP is 160 hours and it's stil going :)) Yep, weakup Swann, this will never happen.

I'm dreaming along with you, to be honest I don't want to wake up... ;)

For this price definetely better solution is to buy 3DBuzz xna101 course that will give you big kickstart into programming.

I think for me personally that's a bit to much for the moment, but agree that 3Dbuzz gives a very solid foundation. Though sometimes they can speed things up a bit, I'm really looking forward to watch the shader building video's they offer.

Link to comment
Share on other sites

My biggest issue with the code that I saw inside this asset was the sheer amount of redundancy and incorrect practices. There's also a lot of clutter. Without having seen the completed asset I can't fully comment but there just seemed to be a bunch of stuff that was useless or could have very easily been done better better, with less code and been more readable.

I took the bit of code in the posted asset, ripped like 40 or 50 lines out just by replacing them with better ways to do some stuff, as well as added some more stuff to make it much more readable. If someone has a completed version of the asset they wouldn't mind sharing I'd love to give it a bit of a rewrite to show people different ways to do things.

Edit: Also, I should mention that you can really learn a fair amount of things by reading the bits and pieces in the help docs, as well as the HOM cookbook. Most of the common things that are done are discussed in there and there's some pretty good examples. If you are interested in object level transforms with matrices and such I also recommend taking a look at the help for hou.ObjNode.setWorldTransform. It's got a good beginner explanation of dealing with transform matrices.

Hey Graham,

You and SideFx should do a dvd specifically for how to implement python in Houdini for production.

regards

Link to comment
Share on other sites

I feel like I came across a bit harsh in my previous post. I'm just super passionate about building tools and assets with Houdini and Python and sometimes get a bit excited about certain things when I might disagree or whatnot with the way other people do things. I apologize.

From what I saw in the finished version of the asset there's a few coding type things I'd offer advice for improvements on:

At the beginning of the rotate() function there's a few lines separately evaluating the rotation parameters to variables, then immediately following the rotate parm tuple is stored and used to set the values to 0. Why not just get the tuple, evaluate it and unpack it into 3 variables with one line instead of several.

There's also a few instances where there's calls to hou.node() with the main asset node's path together joined with a "/something" path to a child node to create a full path to get a node. The beauty of node() functions is they can take relative paths. Rather than hou.node(somenode.path() + "/childnode") you could do something more straight forward using hou.Node.node(): somenode.node("childnode"). It will simple look inside itself for that node without the need to use a full operator path. You can also use regular relative paths: somenode.node("../siblingnode").

Sort of related to the unpacking of the rotate parm tuple, there's a couple instances of using the hou.Matrix4.at() function to extract translations. While that is of course a valid way to extract the translations from the transform it just seems a lot simpler to just call hou.Matrix4.extractTranslates() and unpack the values into variables straight away.

Down in the function to rotate a small cube there also seems to be some extra work going in. The rotates are extracted, but then all the rotate parameters are accessed and set individually. Why not just directly set the rotate parm tuple with the rotate value tuple that was just extracted.

Probably my biggest surprise was the fact that the show/hide functionality was implemented as a function that attempted to toggle display toggles on the nodes inside the assets. Unless your asset is unlocked, which there doesn't really seem to be any need for, the moment you try and toggle show/hide you get permission errors.

The nice thing about a project like this is of course the many different ways you could do it. After taking a look at it I got a bunch of interesting ideas on how to do things similarly but with a bit different setup.

One of my first priorities was to remove a bunch of the repetitive code which was pretty easy using some of the stuff I mentioned above. I then wanted to get rid of a bunch of the complex control structures (tons of if/else stuff). One thing I noticed was the operations/comparisons, wrt if the current block was in the row that was being rotated, were largely all the same, but using different variables. I came up with an idea where I could change the representation of rows/columns from "Left", "Right", "Top", "Bottom", etc to be defined more along the axis they are living on and their position along that axis. For example the Left, Right and Mid X groups get renamed to the xaxis and the are assigned a "component" value. Left became -1, Mid X became 0, Right became 1. Basically if it was in the positive, negative or 0 along that axis. Since an axis id and component id could be assigned to all the rotations I found I could generalize down the comparisons. If I was rotating around the x axis, plane value = 0, I could just use the plane value as the index into the position values of the small and main cube to use them as the comparison. I could then use the component as test for if I should do <, ==, or >. 8 if/else down to 3. Also, I didn't like the fact that there were actually 8 identical function calls in all those statements. My idea was then to just remove all those and use a boolean to keep track of we needed to actually rotate the cube. Since I wasn't going to call the function in all those controls I just moved the code out of a function to immediately afterwards. Whenever the rotate +/- 90 buttons are pressed the button passes the plane and component values along for that particular group.

Next I tackled some of the other other more simple functions:

reset() is basically the same except instead of looping over the range to get the parms I just decided to grab them all and iterate using the parmTuplesInFolder() method.

setKey() is similar in that I grabbed and iterated over all the parms. However, a lot of that code was exactly the same setting up 3 key frames for each component of the rotation parameter. Instead I just loop it using the nice fact hou.ParmTuple objects are iterable/indexable and give you hou.Parm objects. I didn't really see need for autoscoping or unlocking them since they are never locked and I don't envision wanting to manually edit the curves.

I completely ditched the showHide function since it didn't work properly and just channel referenced the display flags on the objects to those toggles.

Another thing I ended up doing was getting rid of the "position" objects inside the asset. The one for the main asset wasn't necessary because it had the exact same transforms as the actual asset. For all the small cube ones I decided to move the geometry back to the origin, translate the cubes into position and rotate with a centered pivot. Same exact result but just not relying on separate nodes to help figure out where they are.

And that was really mostly it for a quick first pass of it all. I was pretty happy but a couple things still bothered me. That massive nested control structure in rotateCustom was the biggest culprit, but I actually didn't like my simplified structure in rotate() for figuring out if the things should be transformed.

For the rotate() function the 3 control statements were exactly the same except for the comparison they performed (<, ==, >). Cue the operator module. It provides function objects that are equivalent to all the normal operators. I could then just store the 3 ones I needed in a tuple and use the component once again to grab the right one and call it. Since I only needed one if statement now I could do away with my boolean flag and just throw the rotate code into that block.

rotateCustom() was pretty straight forward using a good little trick I remember Mark E showing me: Faking an if/else statement using a tuple to store your value choices and then putting your conditional into the index operator for it. eg result = ("it's false!", "it's true)[x == 5]. So for here, if the angle was 180, we subtracted the small value, if it was -180 we added the value. If we negate the angle and check if it's greater than 0 we can use that to figure out what value to add: (-0.0001, 0.0001)[-angle > 0]. The value not being 180 was even easier and exactly the same except we don't negate the angle.

I also removed the +/- 180 degree buttons for less clutter. Lots of parms were of course renamed and the greatness of the new naming scheme is still up for debate. Other than fixing the display toggles I totally avoided the custom geometry tab :)

So that's about it I think. The asset I started with had about 130 lines of code and now it was about half that. I briefly commented most of what I mentioned above.

Overall it was a fun little rewrite project and turned out pretty well for not much time spent on it. Maybe I'll try and take a look at the video when I'm not super busy in a couple weeks.

Well that's it for an insight into my coding mind. Generally I like to keep things as simple and clean as possible, but if I can come up with something a bit more complex but still clear to code I go for it. At least in things I show you guys :).

For more insights into my crazy mind I'm teaching a couple free Python classes in a few weeks at SESI LA. I know most of you guys are from outside the LA area so I plan on recording it myself afterwards and throwing it on my website or something.

In closing, once again I feel bad for coming off so strong but I <3 scripting in Houdini and so should you!

object_rubiks_cube.otl

Edited by graham
  • Like 2
Link to comment
Share on other sites

very nice Graham. In defense of the cmivfx tutorial, while I agree with you it had serious problems, I still found it useful for someone entirely new to python in houdini. I have had a lot of experience in software development/algorithms etc., but I had no experience with coding in houdini, so I found it useful. Sometimes the more advanced optimization stuff is not as useful when you are just finding your way around an api. I look forward to looking through your code.

btw, I believe you taught a masters class either last year or the year before at siggraph.. you doing that again ?

-Greg

Link to comment
Share on other sites

Guest Swann

In defense of the cmivfx tutorial, while I agree with you it had serious problems,

I watched Digital Tutors scripting tutorial for RealFlow and it was better than this. THIS MEANS SOMETHING. CMI tutorials always represented high levels but Python videos (both "Empowering Houdini" and "Intro to Scripting") are really bad. All the time I was watching "Intro to Scripting" I had impression that mentor was not prepared to this tutorial and just came back from the party with a idea to make tutorial before he will go sleep, the only problem was that he didn't knew about what to make this tutorial.

Edited by SWANN
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...