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