Newer
Older
import bpy
from bpy.app.handlers import persistent
from .lib import (
getDisplayBackend,
)
# ############################################################
# Commands
# ############################################################
class Commands:
recenter = 'RECENTER'
class SlaveStatus:
non_setup = 0 # initial
dupli = 1 # view3d duplicated
uiless = 2 # view3d without UI
waituser = 3 # waiting for user to move window to HMD
usermoved = 4 # user moved window
fullscreen = 5 # wait a bit to prevent a crash on OSX
ready = 6 # all went well
error = 7 # something didn't work
# ############################################################
# Main Operator
# ############################################################
class VirtualRealityDisplayOperator(bpy.types.Operator):
""""""
bl_idname = "view3d.virtual_reality_display"
bl_label = "Toggle Virtual Reality Display"
bl_description = ""
_hash_slave = -1
_hash_master = -1
action = bpy.props.EnumProperty(
description="",
items=(("ENABLE", "Enable", "Enable"),
("DISABLE", "Disable", "Disable"),
("RECENTER", "Re-Center", "Re-Center tracking data"),
("FULLSCREEN", "Fullscreen", "Make slave fullscreen"),
options={'SKIP_SAVE'},
)
@classmethod
def poll(cls, context):
return context.area.type == 'VIEW_3D'
def modal(self, context, event):
wm = context.window_manager
vr = wm.virtual_reality
area = context.area
if not area:
self.quit(context)
self._quit(context)
return {'FINISHED'}
return {'PASS_THROUGH'}
def invoke(self, context, event):
wm = context.window_manager
vr = wm.virtual_reality
is_enabled = vr.is_enabled
if self.action == 'TOGGLE':
self.action = 'DISABLE' if is_enabled else 'ENABLE'
self.quit(context)
return {'FINISHED'}
else:
self.report({'ERROR'}, "Virtual Reality Display is not enabled")
return {'CANCELLED'}
elif self.action == 'ENABLE':
if is_enabled:
self.report({'ERROR'}, "Virtual Reality Display is already enabled")
return {'CANCELLED'}
if self.init(context):
return {'RUNNING_MODAL'}
else:
# quit right away
vr.is_enabled = False
elif self.action == 'RECENTER':
vr.command_push(Commands.recenter)
return {'FINISHED'}
elif self.action == 'FULLSCREEN':
vr.command_push(Commands.fullscreen)
return {'FINISHED'}
else:
assert False, "action \"{0}\" not implemented".format(self.action)
return {'CANCELLED'}
def quit(self, context):
"""garbage collect"""
# change it so the original modal operator will clean things up
wm = context.window_manager
def _quit(self, context):
"""actual quit"""
if self._timer:
wm = context.window_manager
wm.event_timer_remove(self._timer)
self._timer = None
if self._handle:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
if context.area:
context.area.tag_redraw()
def init(self, context):
"""
Initialize the callbacks and the external devices
"""
wm = context.window_manager
vr = wm.virtual_reality
vr.is_enabled = True
vr.error_message = ""
self._hmd = HMD(display_backend, context, self._error_callback)
self._hash_master = hash(context.area)
# setup modal
self._timer = wm.event_timer_add(1.0 / 75.0, context.window) # 75 Hz
self._handle = bpy.types.SpaceView3D.draw_handler_add(self._draw_callback_px, (context,), 'WINDOW', 'POST_PIXEL')
wm.modal_handler_add(self)
if self._hmd.is_direct_mode:
vr.is_slave_setup = True
return self._slaveSetup(context)
return True
def _init(self, context):
color_object = [0, 0]
for i in range(2):
self._hmd.setEye(i)
color_object[i] = self._hmd.color_object
self._preview.init(color_object[0], color_object[1])
if self._slave_status == SlaveStatus.error:
return False
elif self._slave_status == SlaveStatus.non_setup:
ok = self._slaveHook(context, SlaveStatus.dupli)
self._slave_status = SlaveStatus.dupli
elif self._slave_status == SlaveStatus.dupli:
ok = self._slaveHook(context, SlaveStatus.uiless)
self._slave_status = SlaveStatus.waituser
elif self._slave_status == SlaveStatus.waituser:
# waiting for the user input
return True
elif self._slave_status == SlaveStatus.usermoved:
bpy.ops.wm.window_fullscreen_toggle()
self._slave_status = SlaveStatus.fullscreen
elif self._slave_status == SlaveStatus.fullscreen:
context.window_manager.virtual_reality.is_slave_setup = False
ok = self._init(context)
self._slave_status = SlaveStatus.ready
else:
assert False, "_slaveSetup: Slave status \"{0}\" not defined".format(self._slave_status)
if not ok:
self._slave_status = SlaveStatus.error
return ok
def _slaveHook(self, context, mode=''):
hashes = []
for screen in bpy.data.screens:
for area in screen.areas:
if area.type == 'VIEW_3D':
hashes.append(hash(area))
if mode == SlaveStatus.dupli:
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
elif mode == SlaveStatus.uiless:
bpy.ops.screen.screen_full_area(use_hide_panels=True)
else:
assert False, "_slaveHook: Slave status \"{0}\" not defined".format(self._slave_status)
for screen in bpy.data.screens:
for area in screen.areas:
if area.type != 'VIEW_3D':
continue
_hash = hash(area)
try:
hashes.remove(_hash)
except ValueError:
self._hash_slave = _hash
print('Success finding slave')
return True
return False
def _commands(self, context):
"""
Process any pending command from the main window
"""
wm = context.window_manager
vr = wm.virtual_reality
while vr.commands:
command = vr.command_pop()
if command == Commands.recenter:
if self._hmd:
self._hmd.reCenter()
elif command == Commands.fullscreen:
self._slave_status = SlaveStatus.usermoved
self._slaveSetup(context)
elif command == Commands.test:
print("Testing !!!")
else:
assert False, "_commands: command \"{0}\" not implemented"
def _loop(self, context):
"""
Get fresh tracking data and render into the FBO
"""
for i in range(2):
self._hmd.setEye(i)
offscreen_object = self._hmd.offscreen_object
projection_matrix = self._hmd.projection_matrix
modelview_matrix = self._hmd.modelview_matrix
offscreen_object.draw_view3d(projection_matrix, modelview_matrix)
def _drawMaster(self, context):
wm = context.window_manager
vr = wm.virtual_reality
self._loop(context)
if vr.use_preview:
self._preview.loop(vr.preview_scale)
def _drawSlave(self, context):
if self._hmd.is_direct_mode:
if self._slave_status == SlaveStatus.ready:
self._loop(context)
elif self._slave_status == SlaveStatus.waituser:
self._drawDisplayMessage()
else:
self._slaveSetup(context)
def _drawDisplayMessage(self):
"""Message telling user to move the window the HMD display"""
TODO
def _draw_callback_px(self, context):
callback function, run every time the viewport is refreshed
area = context.area
hash_area = hash(area)
self._drawSlave(context)
elif hash_area == self._hash_master:
self._drawMaster(context)
def _error_callback(self, message, is_fatal):
"""
Error handler, called from HMD class
"""
context = bpy.context
wm = context.window_manager
vr = wm.virtual_reality
if is_fatal:
self.report({'ERROR'}, message)
self.quit(context)
vr.error_message = message
# ############################################################
# Global Properties
# ############################################################
from bpy.props import (
BoolProperty,
CollectionProperty,
EnumProperty,
StringProperty,
IntProperty,
)
class VirtualRealityCommandInfo(bpy.types.PropertyGroup):
action = EnumProperty(
name="Action",
items=(("NONE", "None", ""),
(Commands.recenter, "Re-Center", ""),
(Commands.test, "Test", ""),
),
default="NONE",
)
is_enabled = BoolProperty(
use_preview = BoolProperty(
name="Preview",
default=False,
)
preview_scale = IntProperty(
name="Preview Scale",
min=0,
max=100,
subtype='PERCENTAGE',
error_message = StringProperty(
tracking_mode = EnumProperty(
name="Tracking Mode",
description="",
items=(("ALL", "All", ""),
("ROTATION", "Rotation Only", "Ignore positional tracking data"),
("NONE", "None", "No tracking"),
),
default="ALL",
)
is_slave_setup = BoolProperty(
default = False,
)
commands = CollectionProperty(type=VirtualRealityCommandInfo)
def command_push(self, action):
command = self.commands.add()
command.action = action
def command_pop(self):
command = self.commands[0]
action = command.action
self.commands.remove(0)
return action
while self.commands:
self.commands.remove(0)
self.use_preview = False
self.error_message = ""
self.is_enabled = False
# ############################################################
# Callbacks
# ############################################################
@persistent
def virtual_reality_load_pre(dummy):
wm = bpy.context.window_manager
@persistent
def virtual_reality_load_post(dummy):
wm = bpy.context.window_manager
# ############################################################
# Un/Registration
# ############################################################
def register():
bpy.app.handlers.load_pre.append(virtual_reality_load_pre)
bpy.app.handlers.load_pre.append(virtual_reality_load_post)
bpy.utils.register_class(VirtualRealityDisplayOperator)
bpy.utils.register_class(VirtualRealityCommandInfo)
bpy.utils.register_class(VirtualRealityInfo)
bpy.types.WindowManager.virtual_reality = bpy.props.PointerProperty(
name="virtual_reality",
type=VirtualRealityInfo,
options={'HIDDEN'},
)
def unregister():
bpy.app.handlers.load_pre.remove(virtual_reality_load_pre)
bpy.app.handlers.load_pre.remove(virtual_reality_load_post)
bpy.utils.unregister_class(VirtualRealityDisplayOperator)
del bpy.types.WindowManager.virtual_reality
bpy.utils.unregister_class(VirtualRealityInfo)
bpy.utils.unregister_class(VirtualRealityCommandInfo)