[py] Custom event profiling in HDA? [old title: Some questions about HDAs creation]

   1184   3   1
User Avatar
Member
900 posts
Joined: Feb. 2016
Offline
I was inspecting the wedge node, which has a Python Module that controls the asset and launch the render.
I have few questions:

  1. The functions inside the python module are not called by any parameter of the wedge. There's no callback script in any of the parms. I thought you should invoke the functions of the python module by using something like ‘hou.phm().myFunction()’
    How are the functions in that python module invoked when you press the Render Wedges button?
  2. Render Wadges button has a tag “takecontrol” set to “always”. What does it mean?
  3. In the python module I see a lot of ‘node.parm(“myparm”).eval()’, but I can't find anywhere the node object being defined with the classic ‘node = hou.pwd()’. Same for ‘rendernode’. How is that possible?
  4. Could you create an environment variable with python or you can only do it through hou.hscript()
  5. How does the wedge actually launch the output driver? I guess it's that ‘rendernode.render()’ in the last lines of the python module, right? Is this ‘render()’ function documented anywhere? I can't happen to find it under the hou.Node help page [www.sidefx.com]

Thanks for any help!
I copy paste the pyhton module of the Wedge Node, so you can have a look at the code I'm talking about.
Cheers


import random

def setenvvariable(var, val):
    hou.hscript("set %s = %s" % (var, val))
    hou.hscript("varchange")

def createwedge(channame,
                englishname,
                min, max,
                steps):
    if englishname == "":
        # Extract the raw channel name
        # to use as our name if the user
        # provided none
        englishname = channame.rpartition("/")[2]
    result = []
    for i in range(steps):
        if steps > 1:
            v = ((max - min) / (steps-1)) * i
        else:
            v = (max - min) / 2.0

        v += min;
        prefix = "_%s_%f" % (englishname, v)
        result.append( (channame, prefix, v) )
    return result

def mergewedge(allwedge, wedge):
    result = []
    for w in wedge:
        for a in allwedge[:]:
            a = a[:]
            a.append(w)
            result.append(a)
    return result

def getwedgenames(node):
    wedges, stashedparms, errormsg = getwedges(node)
    
    names = []
    for wedge in wedges:
        names.append(_calculatewedgename(node, wedge))
    
    return names

def _calculatewedgename(node, wedge_channels):
    wedge_name = node.parm("prefix").eval()

    for channel in wedge_channels:
        channel_prefix = channel[1]
        wedge_name += channel_prefix
    
    return wedge_name

def applyspecificwedge(node, wl):
    wedgestring = _calculatewedgename(node, wl)
    for wedge in wl:
        (cname, prefix, val) = wedge
        parm = node.parm(cname)
        if parm is not None:
            # Check if the parm is integer.
            ptype = parm.parmTemplate().type()
            if ptype == hou.parmTemplateType.Int:
                val = int(val)
            if ptype == hou.parmTemplateType.Menu:
                val = int(val)
            if ptype == hou.parmTemplateType.Toggle:
                val = int(val)
            parm.set(val)
    # Set the wedge environment variable
    setenvvariable("WEDGE", wedgestring)

def buildrandom(chanlist, namelist, rangelist):
    wedge = []

    # A sure sign I should have passed a list of tuples...
    for (chan, englishname, r) in zip(chanlist, namelist, rangelist):
        (a, b) = r
        v = random.uniform(a, b)
        if englishname == "":
            # Extract the raw channel name
            # to use as our name if the user
            # provided none
            englishname = chan.rpartition("/")[2]
        prefix = "_%s_%f" % (englishname, v)
        wedge.append( (chan, prefix, v) )

    return wedge

def getwedges(node):
    numparam = node.parm("wedgeparams").eval()

    stashedparms = []
    errormsg = ""

    if node.parm("wedgemethod").eval() == 0:
        if node.parm("random").eval() == 0:
            allwedge = [[]]
            for p in range(numparam):
                chan = node.parm("chan%d" % (p+1,)).eval()
                name = node.parm("name%d" % (p+1,)).eval()
                if chan != "":
                    parm = node.parm(chan)
                    if parm is None:
                        errormsg += "Cannot find channel %s\n" % (chan, )
                        continue

                    wedge = createwedge(
                        chan,
                        name,
                        node.parm("range%dx" % (p+1,)).eval(),
                        node.parm("range%dy" % (p+1,)).eval(),
                        node.parm("steps%d" % (p+1,)).eval())
        
                    stashedparms.append((chan, parm.eval()))

                    # more an outerproduct than a merge.
                    allwedge = mergewedge(allwedge, wedge)
        else:
            allwedge = []
            chanlist = []
            namelist = []
            rangelist = []
            for p in range(numparam):
                chan = node.parm("chan%d" % (p+1,)).eval()
                name = node.parm("name%d" % (p+1,)).eval()
                if chan != "":
                    parm = node.parm(chan)
                    if parm is None:
                        errormsg += "Cannot find channel %s\n" % (chan, )
                        continue

                    chanlist.append(chan)
                    namelist.append(name)
                    rangelist.append( (node.parm("range%dx" % (p+1,)).eval(),
                                       node.parm("range%dy" % (p+1,)).eval()) )
                    stashedparms.append((chan, parm.eval()))

            random.seed(node.parm("seed").eval())
            nsample = node.parm("numrandom").eval()
            for lvar in range(nsample):
                wedge = buildrandom(chanlist, namelist, rangelist)
                allwedge.append(wedge)
    else:   # Must be by take.
        allwedge = []
        rendernode = None
        if len(node.inputs()) > 0:
            rendernode = node.inputs()[0]
            if rendernode:
                renderpath = rendernode.path()
        if rendernode is None:
            renderpath = node.parm("driver").eval()
            rendernode = node.node(renderpath)
        basetake = node.parm("roottake").eval()
        (takelist, e) = hou.hscript("takels -i -q -p %s" % (basetake,))
        takelist = takelist.strip()
        if takelist == "":
            errormsg += "No takes found as child of \"%s\".\n" % (basetake,)
        takelist = takelist.split("\n")
        chan = renderpath + "/take"
        stashedparms.append((chan, node.parm(chan).eval()))
        for take in takelist:
            allwedge.append( [(chan, "_" + take, take)] )

    return allwedge, stashedparms, errormsg

def applywedge(node):
    allwedge, stashedparms, errormsg = getwedges(node)

    rendernode = None
    if len(node.inputs()) > 0:
        rendernode = node.inputs()[0]
        if rendernode:
            renderpath = rendernode.path()
    if rendernode is None:
        renderpath = node.parm("driver").eval()
        rendernode = node.node(renderpath)
        
    if rendernode is None:
        errormsg += "Cannot find output driver %s\n" % (renderpath,)

    # Extract specified single wedge
    wedgenum = 0
    if node.parm("wrange").eval() == 1:
        wedgenum = node.parm("wedgenum").eval()
        if wedgenum >= len(allwedge):
            errormsg += "Requested wedge %d greater than total wedges %d.\n" % (wedgenum, len(allwedge))
        else:
            allwedge = [allwedge[wedgenum]]

    if errormsg != "":
        if hou.isUIAvailable():
            hou.ui.displayMessage("Errors:\n" + errormsg)
        else:
            print "Errors: " + errormsg

        return

    # Disable background rendering
    fgparm = None
    if node.parm("blockbackground").eval() == 1:
        fgparm = rendernode.parm("soho_foreground")
    
    if fgparm is not None:
        oldfgval = fgparm.eval()
        fgparm.set(True)

    # apply individual wedge
    for wl in allwedge:
        setenvvariable("WEDGENUM", str(wedgenum))
        applyspecificwedge(node, wl)
        rendernode.render()
        wedgenum = wedgenum + 1

    # Restore background rendering
    if fgparm is not None:
        fgparm.set(oldfgval)

    # Restore our settings to pre-wedge states.
    for (chan, val) in stashedparms:
        parm = node.parm(chan)
        parm.set(val)
    # Clear out the environment
    setenvvariable("WEDGE", "")
Edited by Andr - Feb. 21, 2019 09:17:10
User Avatar
Member
8532 posts
Joined: July 2007
Offline
Andr
1.
How are the functions in that python module invoked when you press the Render Wedges button?
have a look inside, there is a Shell ROP with pre-frame script calling the function
hou.node('..').hdaModule().applywedge(hou.node('..'))

Andr
3.
In the python module I see a lot of ‘node.parm(“myparm”).eval()’, but I can't find anywhere the node object being defined with the classic ‘node = hou.pwd()’. Same for ‘rendernode’. How is that possible?
node is passed as an argument to the functions
rendernode is assigned in the code as you can see either from inputs or by other logic

Andr
4.
Could you create an environment variable with python or you can only do it through hou.hscript()
import os
os.environ['MYVAR'] = "something"

Andr
5.
How does the wedge actually launch the output driver?
see 1.
it simply run the Shell ROP, which runs it as a pre-frame script
Tomas Slancik
FX Supervisor
Method Studios, NY
User Avatar
Member
900 posts
Joined: Feb. 2016
Offline
Thanks a lot Thomas, I didn't realize the wedge node is actually a subnetwork.
Now I understand better the logic.



Regarding question 5, I've found that render() function is documented in the RopNode class help page.
Glad to know that there are no hidden commands
User Avatar
Member
900 posts
Joined: Feb. 2016
Offline
Follow up with one question:
As I'm doing my custom hda wedger, I'd like the user to define a block of nodes of its network that will go through the wedging process, in order to evaluate the cooking performance of that particular block through every iteration of paramaters.

My idea was to create a new profile in the pythonModule of my wedger: hou.perfMon.startProfile(), then set wedge parameters, launch renderNode.render(), and finally profile.stop()

Duty of the user would be to use two additional python sop nodes, to be inserted in the network as delimiters of the event to profile.
“Start node” would create a new hou.perfMon.startEvent() , and downstream the “End Node” would close it.
It seems to be a very bad idea since it crashed houidni very badly resulting even in corrupting the .hip file.


Does anybody know the workflow to achieve it?
Maybe should I try to create and stop the profile already in the python sop nodes, without messing with hda python module?

Thanks for any help!
Edited by Andr - Feb. 21, 2019 09:10:45
  • Quick Links