git.lucas.co / hou-control
git clone https://git.lucas.co/hou-control.git

viewer_states/keycam.py (16.5K)

  1 import hou
  2 from hc import HCCam, HCSettings, HCGeo, HCGuides, HCSceneViewer
  3 
  4 
  5 # Required to pass in hou.SceneViewer object
  6 class State(object):
  7     def __init__(self, state_name, scene_viewer):
  8         self.options = {
  9             'center_on_geo': 1,
 10             'lock_cam': 1,
 11             'reset': 0
 12         }
 13 
 14         # Check for cam node
 15         if not hou.node('/obj/keycam'):
 16             cam_node = hou.node('/obj').createNode('cam')
 17             cam_node.setName('keycam')
 18             cam_node.parm('xOrd').set(0)
 19         self.cam_node = hou.node('/obj/keycam')
 20 
 21         # Get scene viewer
 22         self.scene_viewer = HCSceneViewer(scene_viewer)
 23 
 24         # Assign state name
 25         self.state_name = state_name
 26 
 27         # Setup hc cam
 28         self.cam = HCCam(self.cam_node, self.scene_viewer)
 29         prefs = HCSettings().prefs().get('keycam')
 30         self.cam.delta_r = prefs.get('units').get('delta_r')
 31         self.cam.delta_t = prefs.get('units').get('delta_t')
 32 
 33 
 34 
 35         # Setup default cam
 36         # self.default_cam = DefaultCam(self, hou_default_cam)
 37 
 38         # Initialize empty objects
 39         self.kwargs = None
 40         self.guides = None
 41         self.hud = None
 42 
 43         # Status message
 44         context = self.scene_viewer.pwd().childCat()
 45         context_map = {
 46             'Object': 'Keycam: obj context',
 47             'Sop': 'Keycam: sop context',
 48             'Lop': 'Keycam: lop context'
 49         }
 50         hou.ui.setStatusMessage(context_map[context])
 51 
 52     def onGenerate(self, kwargs):
 53         self.kwargs = kwargs
 54         self.guides = HCGuides(self)
 55         # Prevent exiting state when current node changes
 56         self.kwargs['state_flags']['exit_on_node_select'] = False
 57         self.guides.update()
 58         self.hud = Hud(self)
 59         self.hud.update()
 60         
 61         # Register viewer event callback for resizing
 62         self.scene_viewer.hou_tab.addEventCallback(self._onViewerEvent)
 63 
 64     def onDraw(self, kwargs):
 65         self.guides.draw(kwargs)
 66 
 67     def onExit(self, kwargs):
 68         # Cleanup callback
 69         self.scene_viewer.hou_tab.removeEventCallback(self._onViewerEvent)
 70         for viewport in self.scene_viewer.allViewports():
 71             viewport.lockCameraToView(False)
 72 
 73     def _onViewerEvent(self, **kwargs):
 74         event_type = kwargs.get('event_type')
 75         if event_type == hou.sceneViewerEvent.SizeChanged:
 76             self.cam.fitAspectRatio()
 77             self.guides.update()
 78 
 79     def onKeyEvent(self, kwargs):
 80         """Keys are now handled via PluginHotkeyDefinitions and onMenuAction."""
 81         return False
 82 
 83     def onKeyboardEvent(self, kwargs):
 84         return self.onKeyEvent(kwargs)
 85 
 86     def onMenuAction(self, kwargs):
 87         menu_item = kwargs['menu_item']
 88         
 89         action_map = {
 90             'frame':          self.cam.frame,
 91             'reset':          self.cam.reset,
 92             'rotate_left':    self.cam.rotateLeft,
 93             'rotate_right':   self.cam.rotateRight,
 94             'rotate_up':      self.cam.rotateUp,
 95             'rotate_down':    self.cam.rotateDown,
 96             'trans_left':     self.cam.translateLeft,
 97             'trans_right':    self.cam.translateRight,
 98             'trans_up':       self.cam.translateUp,
 99             'trans_down':     self.cam.translateDown,
100             'toggle_proj':    self.cam.toggleProjection,
101             'zoom_persp_in':  lambda: self.cam.zoom('in'),
102             'zoom_persp_out': lambda: self.cam.zoom('out'),
103             'zoom_ortho_in':  lambda: self.cam.zoomOrtho('in'),
104             'zoom_ortho_out': lambda: self.cam.zoomOrtho('out'),
105         }
106 
107         if menu_item in action_map:
108             res = action_map[menu_item]()
109             self.guides.update()
110             return res
111         
112         # Handle Guide toggles
113         if hasattr(self.guides, menu_item):
114             # Update the local attribute
115             setattr(self.guides, menu_item, kwargs[menu_item])
116             self.guides.update()
117             return True
118             
119         return False
120 
121     def onMenuPreOpen(self, kwargs):
122         menu_id = kwargs['menu']
123         menu_item_states = kwargs['menu_item_states']
124         guides = self.guides
125 
126         # Set checkbox states when opening guide menu
127         if menu_id == 'guide_menu':
128             menu_item_states['cam_axis']['value'] = bool(guides.cam_axis)
129             menu_item_states['pivot_axis']['value'] = bool(guides.pivot_axis)
130             menu_item_states['bbox']['value'] = bool(guides.bbox)
131             menu_item_states['perim']['value'] = bool(guides.perim)
132             menu_item_states['pivot_2d']['value'] = bool(guides.pivot_2d)
133             menu_item_states['pivot_3d']['value'] = bool(guides.pivot_3d)
134             menu_item_states['ray']['value'] = bool(guides.ray)
135 
136     def onParmChangeEvent(self, kwargs):
137         parm_map = {
138             't':          (self.cam, 't'),
139             'p':          (self.cam, 'p'),
140             'r':          (self.cam, 'r'),
141             'ow':         (self.cam, 'ow'),
142             'zoom':       (self.cam, 'zoom'),
143             'target':     (self.cam, 'target'),
144             'delta_r':    (self.cam, 'delta_r'),
145             'delta_t':    (self.cam, 'delta_t'),
146             'delta_zoom': (self.cam, 'delta_zoom'),
147             # 'delta_ow':   (self.cam, 'delta_ow'),
148             # 'layout':     (self.cam, 'layout'),
149             'viewport':   (self.cam, 'viewport'),
150             # 'view':       (self.cam, 'view'),
151             # 'camera':     (self.cam, 'camera'),
152         }
153         obj = parm_map[kwargs['parm_name']][0]
154         attr = parm_map[kwargs['parm_name']][1]
155         setattr(obj, attr, kwargs['parm_value'])
156         # self.guides.update
157 
158 
159 class Hud:
160     def __init__(self, state):
161         self.state = state
162         self.scene_viewer = state.scene_viewer
163         self.template = {
164             'title': 'Keycam',
165             'rows': [
166                 {
167                     'id':    'layout',
168                     'type':  'plain',
169                     'label': 'Layout',
170                     'value': 'Single',
171                     'key':   'Ctrl + L'
172                 },
173                 {
174                     'id':    'layout_g',
175                     'type':  'choicegraph',
176                     'count': 8
177                 },
178                 {
179                     'id':    'viewport',
180                     'type':  'plain',
181                     'label': 'Viewport',
182                     'value': '0',
183                     'key':   'Ctrl + V'
184                 },
185                 {
186                     'id':    'viewport_g',
187                     'type':  'choicegraph',
188                     'count': 4
189                 },
190                 {
191                     'id':    'view',
192                     'type':  'plain',
193                     'label': 'View',
194                     'value': 'Perspective',
195                     'key':   'V'
196                 },
197                 {
198                     'id':    'view_g',
199                     'type':  'choicegraph',
200                     'count': 8
201                 },
202                 {
203                     'id':    'target',
204                     'type':  'plain',
205                     'label': 'Target',
206                     'value': 'Camera',
207                     'key':   'T'
208                 },
209                 {
210                     'id':    'target_g',
211                     'type':  'choicegraph',
212                     'count': 2
213                 },
214                 {
215                     'id':    'vis',
216                     'type':  'plain',
217                     'label': 'Vis'
218                 },
219                 {
220                     'id':    'focus',
221                     'type':  'plain',
222                     'label': 'Focus',
223                     'value': 0
224                 },
225                 {
226                     'id':    'focus_g',
227                     'type':  'choicegraph',
228                     'count': 10
229                 }
230             ]
231         }
232 
233     def update(self):
234         layout_map = {
235             'geometryViewportLayout.DoubleSide':        2,
236             'geometryViewportLayout.DoubleStack':       2,
237             'geometryViewportLayout.Quad':              4,
238             'geometryViewportLayout.QuadBottomSplit':   4,
239             'geometryViewportLayout.QuadLeftSplit':     4,
240             'geometryViewportLayout.TripleBottomSplit': 3,
241             'geometryViewportLayout.TripleLeftSplit':   3,
242             'geometryViewportLayout.Single':            1,
243         }
244         layout = self.scene_viewer.layout()
245         n_viewports = layout_map[str(layout)]
246         self.template['rows'][3]['count'] = n_viewports
247 
248         # Updates
249         hou_scene_viewer = self.scene_viewer.hou_tab
250         hou_scene_viewer.hudInfo(template=self.template)
251         updates = {
252             'layout':   str(self.scene_viewer.layout())[23:],
253             'layout_g': self.scene_viewer.layouts().index(self.scene_viewer.layout()),
254         }
255         hou_scene_viewer.hudInfo(hud_values=updates)
256 
257 
258 def createViewerStateTemplate():
259     template = hou.ViewerStateTemplate(
260         type_name='keycam', label='keycam',
261         category=hou.objNodeTypeCategory(),
262         contexts=[hou.sopNodeTypeCategory()]
263     )
264 
265     template.bindFactory(State)
266     template.bindIcon('DESKTOP_application_sierra')
267 
268     # Define Commands and Default Bindings
269     hotkeys = hou.PluginHotkeyDefinitions()
270     ctx = 'h.pane.gview.state.obj.keycam'
271     hotkeys.addContext(ctx, 'HC Keycam', 'Hotkeys for the HC Keycam viewer state')
272     
273     commands = {
274         'rotate_left':   ('Rotate Left',   ['h']),
275         'rotate_right':  ('Rotate Right',  ['l']),
276         'rotate_up':     ('Rotate Up',     ['k']),
277         'rotate_down':   ('Rotate Down',   ['j']),
278         'trans_left':    ('Translate Left', ['Shift+h']),
279         'trans_right':   ('Translate Right',['Shift+l']),
280         'trans_up':      ('Translate Up',   ['Shift+k']),
281         'trans_down':    ('Translate Down', ['Shift+j']),
282         'frame':         ('Frame',         ['f']),
283         'toggle_proj':   ('Toggle Projection', ['o']),
284         'zoom_persp_in': ('Zoom In (Persp)',   ['=']),
285         'zoom_persp_out':('Zoom Out (Persp)',  ['-']),
286         'zoom_ortho_in': ('Zoom In (Ortho)',   ['Shift+=']),
287         'zoom_ortho_out':('Zoom Out (Ortho)',  ['Shift+-']),
288         'reset':         ('Reset',             ['r']),
289     }
290     
291     for cmd_id, (label, defaults) in commands.items():
292         symbol = f"{ctx}.{cmd_id}"
293         hotkeys.addCommand(symbol, label, "")
294         hotkeys.addDefaultBinding(ctx, symbol, defaults)
295     
296     template.bindHotkeyDefinitions(hotkeys)
297 
298     # Parameters
299 
300     template.bindParameter(
301         hou.parmTemplateType.Menu,
302         name='layout',
303         label='Layout',
304         default_value='single',
305         menu_items=[
306             ('doubleside',        'DoubleSide'),
307             ('doublestack',       'DoubleStack'),
308             ('quad',              'Quad'),
309             ('quadbottomsplit',   'QuadBottomSplit'),
310             ('quadleftsplit',     'QuadLeftSplit'),
311             ('single',            'Single'),
312             ('triplebottomsplit', 'TripleBottomSplit'),
313             ('tripleleftsplit',   'TripleLeftSplit')
314         ]
315     )
316 
317     template.bindParameter(
318         hou.parmTemplateType.Menu,
319         name='viewport',
320         label='Viewport',
321         default_value='center',
322         menu_items=[('center', 'Center')]
323     )
324 
325     template.bindParameter(
326         hou.parmTemplateType.Menu,
327         name='view',
328         label='View',
329         default_value='persp',
330         menu_items=[
331             ('persp',  'Perspective'),
332             ('top',    'Top'),
333             ('front',  'Front'),
334             ('right',  'Right'),
335             ('uv',     'UV'),
336             ('bottom', 'Bottom'),
337             ('back',   'Back'),
338             ('left',   'Left')
339         ]
340     )
341 
342     template.bindParameter(
343         hou.parmTemplateType.Menu,
344         name='camera',
345         label='Camera',
346         default_value='keycam',
347         menu_items=[
348             ('keycam',  'Keycam'),
349             ('default', 'Default'),
350             ('other',   'Other')
351         ],
352         toolbox=False
353     )
354 
355     template.bindParameter(
356         hou.parmTemplateType.Menu,
357         name='target',
358         label='Target',
359         default_value='cam',
360         menu_items=[
361             ('cam',   'Camera'),
362             ('pivot', 'Pivot')
363         ],
364         toolbox=False
365     )
366 
367     template.bindParameter(
368         hou.parmTemplateType.Separator,
369         toolbox=False
370     )
371 
372     template.bindParameter(
373         hou.parmTemplateType.Float,
374         name='t',
375         label='Translation',
376         num_components=3,
377         toolbox=False
378     )
379 
380     # template.bindParameter(
381     #     hou.parmTemplateType.Float,
382     #     name='r',
383     #     label='Rotation',
384     #     num_components=3,
385     #     toolbox=False
386     # )
387 
388     template.bindParameter(
389         hou.parmTemplateType.Float,
390         name='p',
391         label='Pivot',
392         num_components=3,
393         toolbox=False
394     )
395 
396     template.bindParameter(
397         hou.parmTemplateType.Float,
398         name='zoom',
399         label='Zoom',
400         num_components=1,
401         toolbox=False
402     )
403 
404     template.bindParameter(
405         hou.parmTemplateType.Float,
406         name='ow',
407         label='Ortho Width',
408         num_components=1,
409         toolbox=False
410     )
411 
412     template.bindParameter(
413         hou.parmTemplateType.Separator,
414         toolbox=False
415     )
416 
417     template.bindParameter(
418         hou.parmTemplateType.Float,
419         name='delta_t',
420         label='Delta T',
421         default_value=1.0,
422         min_limit=0,
423         max_limit=10.0
424     )
425 
426     template.bindParameter(
427         hou.parmTemplateType.Float,
428         name='delta_r',
429         label='Delta R',
430         default_value=15.0,
431         min_limit=-180.0,
432         max_limit=180.0
433     )
434 
435     template.bindParameter(
436         hou.parmTemplateType.Float,
437         name='delta_zoom',
438         label='Delta Zoom',
439         default_value=1.0,
440         min_limit=0,
441         max_limit=10.0
442     )
443 
444     template.bindParameter(
445         hou.parmTemplateType.Float,
446         name='delta_ow',
447         label='Delta OW',
448         default_value=1.0,
449         min_limit=0,
450         max_limit=10.0
451     )
452 
453     """ Context Menu """
454 
455     menu = hou.ViewerStateMenu('keycam_menu', 'Keycam Menu')
456     ctx = 'h.pane.gview.state.obj.keycam'
457     
458     menu.addActionItem('frame', 'Frame', hotkey=f'{ctx}.frame')
459     menu.addActionItem('reset', 'Reset', hotkey=f'{ctx}.reset')
460 
461     set_view_menu = hou.ViewerStateMenu('set_view_menu', 'Set View')
462     set_view_menu.addActionItem('top',    'Top')
463     set_view_menu.addActionItem('bottom', 'Bottom')
464     set_view_menu.addActionItem('front',  'Front')
465     set_view_menu.addActionItem('back',   'Back')
466     set_view_menu.addActionItem('left',   'Left')
467     # set_view_menu.addActionItem('right', 'Right')
468     menu.addMenu(set_view_menu)
469 
470     guide_menu = hou.ViewerStateMenu('guide_menu', 'Guides')
471     guide_menu.addToggleItem('bbox',       'Bbox',        0)
472     guide_menu.addToggleItem('cam_axis',   'Camera Axis', 0)
473     guide_menu.addToggleItem('pivot_axis', 'Pivot Axis',  0)
474     guide_menu.addToggleItem('perim',      'Perimeter',   0)
475     guide_menu.addToggleItem('pivot_2d',   '2D Pivot',    0)
476     guide_menu.addToggleItem('pivot_3d',   '3D Pivot',    0)
477     guide_menu.addToggleItem('ray',        'Ray',         0)
478     menu.addMenu(guide_menu)
479 
480     menu.addSeparator()
481     menu.addToggleItem('toggle_proj', 'Toggle Projection', 0, hotkey=f'{ctx}.toggle_proj')
482     
483     # We add the remaining hotkey actions to a submenu to keep them registered
484     # but out of the main menu if desired.
485     nav_menu = hou.ViewerStateMenu('nav_menu', 'Navigation')
486     nav_menu.addActionItem('rotate_left',   'Rotate Left',    hotkey=f'{ctx}.rotate_left')
487     nav_menu.addActionItem('rotate_right',  'Rotate Right',   hotkey=f'{ctx}.rotate_right')
488     nav_menu.addActionItem('rotate_up',     'Rotate Up',      hotkey=f'{ctx}.rotate_up')
489     nav_menu.addActionItem('rotate_down',   'Rotate Down',    hotkey=f'{ctx}.rotate_down')
490     nav_menu.addSeparator()
491     nav_menu.addActionItem('trans_left',    'Translate Left',  hotkey=f'{ctx}.trans_left')
492     nav_menu.addActionItem('trans_right',   'Translate Right', hotkey=f'{ctx}.trans_right')
493     nav_menu.addActionItem('trans_up',      'Translate Up',    hotkey=f'{ctx}.trans_up')
494     nav_menu.addActionItem('trans_down',    'Translate Down',  hotkey=f'{ctx}.trans_down')
495     nav_menu.addSeparator()
496     nav_menu.addActionItem('zoom_persp_in',  'Zoom In (Persp)',  hotkey=f'{ctx}.zoom_persp_in')
497     nav_menu.addActionItem('zoom_persp_out', 'Zoom Out (Persp)', hotkey=f'{ctx}.zoom_persp_out')
498     nav_menu.addActionItem('zoom_ortho_in',  'Zoom In (Ortho)',  hotkey=f'{ctx}.zoom_ortho_in')
499     nav_menu.addActionItem('zoom_ortho_out', 'Zoom Out (Ortho)', hotkey=f'{ctx}.zoom_ortho_out')
500     
501     menu.addMenu(nav_menu)
502     
503     template.bindMenu(menu)
504 
505     # Ok
506     return template
507