1
2 '''
3 Class definitions for a persistance instance style Houdini jobtype
4 '''
5
6
7
8
9
10 import sys
11 import os
12
13 try:
14 import qb
15 except:
16 if 'QBDIR' in os.environ:
17 QBDIR = os.environ['QBDIR']
18 else:
19 if os.name == 'posix':
20 if os.uname()[0] == 'Darwin':
21 QBDIR = '/Applications/pfx/qube'
22 else:
23 QBDIR = '/usr/local/pfx/qube'
24
25 sys.path.append('%s/api/python' % QBDIR)
26 import qb
27
28 try:
29 from qb import frontend
30 from qb.frontend import pythonFrontEnd as pythonFrontEnd
31 except ImportError:
32 from AppUI import frontend
33 from AppUI.frontend import pythonFrontEnd as pythonFrontEnd
34
35
37 '''
38 A base class for all Houdini dynamic allocation jobs
39 '''
41 super(HoudiniPythonJob, self).__init__()
42
43 self['kind'] = 'houdini'
44 self['prototype'] = 'pyHoudini'
45
46 pkg = {
47 'smartAgenda': True,
48 'HFS': HFS,
49 'regex_errors': 'No licenses could be found to run this application',
50 'regex_highlights': 'Houdini version.*\d+.*',
51 }
52 self['package'].update(pkg)
53
54 self.addJobCmds('''print 'INFO: Houdini version: %s v%s' % (hou.applicationName(), hou.applicationVersionString() )''')
55
56
58 '''
59 A virtual class for all Houdini hip file jobs
60 '''
62 super(HoudiniHipBaseJob, self).__init__(HFS)
63
64 self.hipFile = hipFile
65 self['package']['hipFile'] = 'QB_CONVERT_PATH(%s)' % self.hipFile
66 self['package']['regex_errors'] += '|Unable to open file|hou.OperationFailed|hou.LoadWarning'
67 self.addJobCmds(self.__getJobSetupCmds())
68
70 '''
71 Build a list of commands necessary to load up a houdini hip and prepare to execute it.
72 '''
73 cmds = []
74 cmds.append("hou.hipFile.load('%s', suppress_save_prompt=True, ignore_load_warnings=True)" % self['package']['hipFile'])
75
76 return cmds
77
78
80 '''
81 A class for Houdini jobs that process one or more ROP nodes
82 '''
83 - def __init__(self, ropPaths, hipFile, frameRange, HFS=''):
84 super(HoudiniRenderRopJob, self).__init__(hipFile, HFS)
85
86 self.frameRange = frameRange
87 self.ropPaths = ropPaths
88
89 self['package']['frameRange'] = self.frameRange
90 self['package']['ropPaths'] = self.ropPaths
91 self['package']['regex_outputPaths'] = '^ROP output: (.*)'
92
93 self['agenda'] = self._buildAgenda()
94
96 '''
97 Create an agenda for the job, complete with per-frame python commands.
98 '''
99 agenda = []
100 ropPaths = [x.strip() for x in self['package']['ropPaths'].split(',')]
101
102 for frame in [x['name'] for x in qb.genframes(self.frameRange)]:
103
104 cmds = ['''
105 for ropPath in %(ropPaths)s:
106 rop = hou.node(ropPath)
107
108 exportIFD = rop.parm('soho_outputmode').evalAsInt()
109 if exportIFD:
110 ropOutput = rop.parm('soho_diskfile').evalAtFrame(%(frame)s)
111 else:
112 ropOutput = rop.parm('vm_picture').evalAtFrame(%(frame)s)
113
114 print 'ROP output: %%s' %% ropOutput
115 rop.render(frame_range=(%(frame)s,%(frame)s))
116 ''' % {'frame': frame, 'ropPaths': ropPaths.__repr__()}
117 ]
118
119 work = {
120 'name': frame,
121 'package': {'commands': cmds}
122 }
123
124 agenda.append(work)
125
126 return agenda
127
128
129 if __name__ == '__main__':
130 '''
131 A "hello world", meant to show how to build and submit a Houdini dynamic allocation job using the pythonChildJob framework.
132 '''
133
134
135
136 frameRange = '1-5'
137
138
139
140 HFS = ''
141
142 hipFile = '/Users/jburk/Documents/houdini/testProject/test.hip'
143 ropPaths = '/out/mantra1,/out/mantra2'
144
145 job = HoudiniRenderRopJob(ropPaths, hipFile, frameRange, HFS)
146 job['reservations'] = 'host.processors=1+'
147 job['cpus'] = 2
148 job['package']['houdiniVersion'] = (11,1,201)
149
150 if '--submit' not in sys.argv:
151 arcSize = qb.archivejob('job.qja', job, format=qb.QB_API_BINARY)
152 print 'job.qja: %s bytes' % arcSize
153 else:
154 for j in qb.submit(job):
155 print 'submitted: %(id)s' % j
156
157 sys.exit()
158