Skip to content

Commit

Permalink
Merge pull request #100 from regcs/alice-lg-2.1
Browse files Browse the repository at this point in the history
Update master to Alice/LG 2.1
  • Loading branch information
regcs authored Sep 16, 2022
2 parents 243ee00 + 7473fb5 commit 67e2839
Show file tree
Hide file tree
Showing 15 changed files with 2,282 additions and 506 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ This add-on was created for the use of Blender with the Looking Glass holographi
## System Requirements
- Windows, Linux, or macOS
- [Blender 2.93.6 or later](https://www.blender.org/download/)
- [Holoplay Service App](https://lookingglassfactory.com/software)
- [Holoplay Service / Looking Glass Bridge](https://lookingglassfactory.com/software)

## Installation

1. [Download](https://lookingglassfactory.com/software) and install the Holoplay Service App on your PC or Mac.
1. [Download](https://lookingglassfactory.com/software) and install the Holoplay Service / Looking Glass Bridge App on your PC or Mac.

2. Download the [zip file of the latest release](https://github.com/regcs/AliceLG/releases/) of this add-on.

Expand All @@ -50,13 +50,13 @@ After the installation you find a _Looking Glass_ tab in each Blender viewport.

- **Quilt Setup & Rendering.** Controls for starting a quilt render.

- **Lightfield Window.** The lightfield / hologram is displayed on your Looking Glass display via the HoloPlay Service App in a separate window. In this category you find options to switch between two different modes for the lightfield Window: _Viewport_ and _Quilt Viewer_. It has the following sub-panel:
- **Lightfield Window.** The lightfield / hologram is displayed on your Looking Glass display via the HoloPlay Service / Looking Glass Bridge App in a separate window. In this category you find options to switch between two different modes for the lightfield Window: _Viewport_ and _Quilt Viewer_. It has the following sub-panel:

- **Shading & Overlay Settings.** If the lightfield window is in _Viewport_ mode, it acts as your holographic Blender viewport. The settings for this (lightfield) viewport are defined here.

### Lightfield Window & Viewport

The lightfield window is the place where the hologram is rendered. It can be opened via a click on the button: _Looking Glass → Lightfield Window_, if you have a Looking Glass connected and HoloPlay Service is running. The lightfield window can operate in two modes:
The lightfield window is the place where the hologram is rendered. It can be opened via a click on the button: _Looking Glass → Lightfield Window_, if you have a Looking Glass connected and HoloPlay Service / Looking Glass Bridge is running. The lightfield window can operate in two modes:

- **Viewport.** In viewport mode, it basically acts like a normal Blender viewport in the Looking Glass - except that it is holographic. You can choose between _Auto_ and _Manual_ refresh mode: In _Auto_ mode, the hologram is re-rendered everytime something in the scene changes, while in _Manual_ mode the hologram is only rendered if you click the refresh button. _NOTE: Due to some limitations of the rendering pipeline of Blender, this mode can be quite slow (< 5 fps). We are working on improving Blender with regard to this and, hopefully, future versions of Blender will increase the live view performance of Alice/LG._

Expand Down
102 changes: 80 additions & 22 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
bl_info = {
"name": "Alice/LG",
"author": "Christian Stolze",
"version": (2, 0, 6),
"version": (2, 1, 0),
"blender": (2, 93, 6),
"location": "View3D > Looking Glass Tab",
"description": "Alice/LG takes your artworks through the Looking Glass (lightfield displays)",
Expand Down Expand Up @@ -253,7 +253,7 @@ def LookingGlassAddonInitHandler(dummy1, dummy2):

# initialize the RenderSettings
# NOTE: This loads the last render settings from the lockfile
RenderSettings(bpy.context.scene, False, LookingGlassAddon.has_lockfile, (bpy.context.preferences.addons[__package__].preferences.render_mode == '1'), blocking=LookingGlassAddon.background)
RenderSettings(bpy.context.scene, False, LookingGlassAddon.has_lockfile, (bpy.context.preferences.addons[__package__].preferences.camera_mode == '1'), blocking=LookingGlassAddon.background)

else:

Expand All @@ -266,12 +266,14 @@ def LookingGlassAddonInitHandler(dummy1, dummy2):
# then update the selected quilt preset from the device's default quilt
if device and preset:
bpy.context.scene.addon_settings.quiltPreset = str(preset)
bpy.context.scene.addon_settings.render_quilt_preset = str(preset)

elif device and not preset:
elif not (device and preset):

# fallback solution, if the default quilt is not found:
# We use the Looking Glass Portrait standard quilt (48 views)
bpy.context.scene.addon_settings.quiltPreset = "4"
bpy.context.scene.addon_settings.render_quilt_preset = "4"

# check if Blender is run in background mode
if LookingGlassAddon.background:
Expand All @@ -289,8 +291,13 @@ def LookingGlassAddonInitHandler(dummy1, dummy2):

else:

# invoke the camera frustum rendering operator
bpy.ops.render.frustum('INVOKE_DEFAULT')
# create and start the frustum and the block renderer
LookingGlassAddon.FrustumRenderer = FrustumRenderer()
LookingGlassAddon.ImageBlockRenderer = BlockRenderer()
LookingGlassAddon.ViewportBlockRenderer = BlockRenderer()

# start the renderers
LookingGlassAddon.FrustumRenderer.start(bpy.context)

# get the active window
LookingGlassAddon.BlenderWindow = bpy.context.window
Expand Down Expand Up @@ -335,7 +342,7 @@ def register():
LookingGlassAddon.background = True

# if NOT all dependencies are satisfied
if LookingGlassAddon.check_dependecies() == False:
if not LookingGlassAddon.check_dependecies():

# register the preferences operators
bpy.utils.register_class(LOOKINGGLASS_OT_install_dependencies)
Expand All @@ -351,6 +358,10 @@ def register():
# or when a new file is loaded
bpy.app.handlers.load_post.append(LookingGlassAddonInitHandler)

# for the addon unregistering we need to remember that we started
# in the dependency installer mode
LookingGlassAddon.external_dependecies_installer = True

else:

# register all basic operators of the addon
Expand All @@ -364,19 +375,38 @@ def register():
# Looking Glass quilt rendering
bpy.utils.register_class(LOOKINGGLASS_OT_render_quilt)

# Looking Glass viewport & camera frustum
# Looking Glass viewport
bpy.utils.register_class(LOOKINGGLASS_OT_render_viewport)
bpy.utils.register_class(LOOKINGGLASS_OT_render_frustum)
bpy.utils.register_class(BlockRenderer.LOOKINGGLASS_OT_update_block_renderer)

keyconfigs_addon = bpy.context.window_manager.keyconfigs.addon
if keyconfigs_addon:
# 3D Viewport
LookingGlassAddon.keymap_view_3d = keyconfigs_addon.keymaps.new(name="3D View", space_type='VIEW_3D')
LookingGlassAddon.keymap_items_view_3d_1 = LookingGlassAddon.keymap_view_3d.keymap_items.new("wm.update_block_renderer", 'MOUSEMOVE', 'ANY')
LookingGlassAddon.keymap_items_view_3d_2 = LookingGlassAddon.keymap_view_3d.keymap_items.new("wm.update_block_renderer", 'LEFTMOUSE', 'ANY')
# Image editor
LookingGlassAddon.keymap_image_editor = keyconfigs_addon.keymaps.new(name="Image", space_type='IMAGE_EDITOR')
LookingGlassAddon.keymap_items_image_editor_1 = LookingGlassAddon.keymap_image_editor.keymap_items.new("wm.update_block_renderer", 'MOUSEMOVE', 'ANY')
LookingGlassAddon.keymap_items_image_editor_2 = LookingGlassAddon.keymap_image_editor.keymap_items.new("wm.update_block_renderer", 'LEFTMOUSE', 'ANY')

# UI elements
# add-on preferences
bpy.utils.register_class(LOOKINGGLASS_PT_preferences)
# add-on panels
# addon panels
bpy.utils.register_class(LOOKINGGLASS_PT_panel_general)
bpy.utils.register_class(LOOKINGGLASS_PT_panel_camera)
bpy.utils.register_class(LOOKINGGLASS_PT_panel_render)
bpy.utils.register_class(LOOKINGGLASS_PT_panel_lightfield)
bpy.utils.register_class(LOOKINGGLASS_PT_panel_overlays_shading)
# addon header buttons: 3D viewport
bpy.utils.register_class(LOOKINGGLASS_PT_panel_blocks_viewport_options)
bpy.utils.register_class(LOOKINGGLASS_HT_button_viewport_blocks)
bpy.types.VIEW3D_HT_header.append(LOOKINGGLASS_HT_button_viewport_blocks.draw_item)
# addon header buttons: image editor
bpy.utils.register_class(LOOKINGGLASS_PT_panel_blocks_imageeditor_options)
bpy.utils.register_class(LOOKINGGLASS_HT_button_imageeditor_blocks)
bpy.types.IMAGE_HT_header.append(LOOKINGGLASS_HT_button_imageeditor_blocks.draw_item)

# log info
LookingGlassAddonLogger.info(" [#] Registered add-on operators in Blender.")
Expand All @@ -393,19 +423,19 @@ def register():
LookingGlassAddonLogger.info(" [#] Done.")

# log info
LookingGlassAddonLogger.info("Connecting to HoloPlay Service ...")
LookingGlassAddonLogger.info("Connecting to Looking Glass Bridge ...")

# create a service using "HoloPlay Service" backend
LookingGlassAddon.service = pylio.ServiceManager.add(pylio.lookingglass.services.HoloPlayService, client_name = LookingGlassAddon.name)
# create a service using "Looking Glass Bridge" backend
LookingGlassAddon.service = pylio.ServiceManager.add(pylio.lookingglass.services.LookingGlassBridge, client_name = LookingGlassAddon.name)

# if a service was added
if type(LookingGlassAddon.service) == pylio.lookingglass.services.HoloPlayService:
if type(LookingGlassAddon.service) == pylio.lookingglass.services.LookingGlassBridge:

# if the service is ready
if LookingGlassAddon.service.is_ready():

# log info
LookingGlassAddonLogger.info(" [#] Connected to HoloPlay Service version: %s" % LookingGlassAddon.service.get_version())
LookingGlassAddonLogger.info(" [#] Connected to Looking Glass Bridge version: %s" % LookingGlassAddon.service.get_version())

else:

Expand Down Expand Up @@ -433,13 +463,13 @@ def register():

# # prepare the error string from the error code
# if (errco == hpc.client_error.CLIERR_NOSERVICE.value):
# errstr = "HoloPlay Service not running"
# errstr = "Looking Glass Bridge not running"
#
# elif (errco == hpc.client_error.CLIERR_SERIALIZEERR.value):
# errstr = "Client message could not be serialized"
#
# elif (errco == hpc.client_error.CLIERR_VERSIONERR.value):
# errstr = "Incompatible version of HoloPlay Service";
# errstr = "Incompatible version of Looking Glass Bridge";
#
# elif (errco == hpc.client_error.CLIERR_PIPEERROR.value):
# errstr = "Interprocess pipe broken"
Expand All @@ -459,17 +489,25 @@ def unregister():
# if the a service for display communication is active
if LookingGlassAddon.service:

# Unregister at the Holoplay Service
# Unregister at Looking Glass Bridge
pylio.ServiceManager.remove(LookingGlassAddon.service)

# log info
LookingGlassAddonLogger.info("Unregister the addon:")

# log info
LookingGlassAddonLogger.info(" [#] Stopping frustum and block renderers.")

# stop the frustum and block renderers
if LookingGlassAddon.FrustumRenderer: LookingGlassAddon.FrustumRenderer.stop()
if LookingGlassAddon.ImageBlockRenderer: LookingGlassAddon.ImageBlockRenderer.stop()
if LookingGlassAddon.ViewportBlockRenderer: LookingGlassAddon.ViewportBlockRenderer.stop()

# log info
LookingGlassAddonLogger.info(" [#] Removing all registered classes.")

# if NOT all dependencies are satisfied
if LookingGlassAddon.check_dependecies() == False:
if not LookingGlassAddon.check_dependecies() or LookingGlassAddon.external_dependecies_installer:

# unregister only the preferences
if hasattr(bpy.types, "LOOKINGGLASS_PT_install_dependencies"): bpy.utils.unregister_class(LOOKINGGLASS_PT_install_dependencies)
Expand All @@ -495,18 +533,38 @@ def unregister():
# Looking Glass quilt rendering
bpy.utils.unregister_class(LOOKINGGLASS_OT_render_quilt)

# Looking Glass viewport & camera frustum
# remove the keymap
keyconfigs_addon = bpy.context.window_manager.keyconfigs.addon
if keyconfigs_addon:
# 3D Viewport
if LookingGlassAddon.keymap_view_3d: LookingGlassAddon.keymap_view_3d.keymap_items.remove(LookingGlassAddon.keymap_items_view_3d_2)
if LookingGlassAddon.keymap_view_3d: LookingGlassAddon.keymap_view_3d.keymap_items.remove(LookingGlassAddon.keymap_items_view_3d_1)
if LookingGlassAddon.keymap_view_3d: keyconfigs_addon.keymaps.remove(LookingGlassAddon.keymap_view_3d)
# Image editor
if LookingGlassAddon.keymap_image_editor: LookingGlassAddon.keymap_image_editor.keymap_items.remove(LookingGlassAddon.keymap_items_image_editor_2)
if LookingGlassAddon.keymap_image_editor: LookingGlassAddon.keymap_image_editor.keymap_items.remove(LookingGlassAddon.keymap_items_image_editor_1)
if LookingGlassAddon.keymap_image_editor: keyconfigs_addon.keymaps.remove(LookingGlassAddon.keymap_image_editor)

# Looking Glass viewport
bpy.utils.unregister_class(BlockRenderer.LOOKINGGLASS_OT_update_block_renderer)
bpy.utils.unregister_class(LOOKINGGLASS_OT_render_viewport)
bpy.utils.unregister_class(LOOKINGGLASS_OT_render_frustum)

# UI elements
bpy.utils.unregister_class(LOOKINGGLASS_PT_preferences)
# addon header buttons
bpy.types.IMAGE_HT_header.remove(LOOKINGGLASS_HT_button_imageeditor_blocks.draw_item)
bpy.types.VIEW3D_HT_header.remove(LOOKINGGLASS_HT_button_viewport_blocks.draw_item)
if hasattr(bpy.types, "LOOKINGGLASS_HT_button_viewport_blocks"): bpy.utils.unregister_class(LOOKINGGLASS_HT_button_viewport_blocks)
if hasattr(bpy.types, "LOOKINGGLASS_HT_button_imageeditor_blocks"): bpy.utils.unregister_class(LOOKINGGLASS_HT_button_imageeditor_blocks)
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_blocks_viewport_options"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_blocks_viewport_options)
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_blocks_imageeditor_options"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_blocks_imageeditor_options)
# addon panels
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_general"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_general)
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_camera"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_camera)
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_render"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_render)
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_lightfield"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_lightfield)
if hasattr(bpy.types, "LOOKINGGLASS_PT_panel_overlays_shading"): bpy.utils.unregister_class(LOOKINGGLASS_PT_panel_overlays_shading)

# preferences
bpy.utils.unregister_class(LOOKINGGLASS_PT_preferences)
# delete all variables
if hasattr(bpy.types.Scene, "addon_settings"): del bpy.types.Scene.addon_settings

Expand Down
55 changes: 39 additions & 16 deletions globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class LookingGlassAddon:
('cv2', 'opencv-python', ''),
('pylightio', 'pylightio', ''),
]
external_dependecies_installer = False

# Blender arguments
blender_arguments = ""
Expand All @@ -74,9 +75,13 @@ class LookingGlassAddon:
# Lockfile
has_lockfile = False

# Was the modal operator for the frustum initialized?
# Was the frustum drawer initialized?
FrustumInitialized = False
FrustumDrawHandler = None
FrustumRenderer = None

# Was the hologram preview drawer initialized?
ImageBlockRenderer = None
ViewportBlockRenderer = None

# The active Window and Viewport the user is currently working in
BlenderWindow = None
Expand All @@ -86,6 +91,18 @@ class LookingGlassAddon:
RenderInvoked = False
RenderAnimation = None

# keymaps and mouse position
keymap_view_3d = None
keymap_items_view_3d_1 = None
keymap_items_view_3d_2 = None
keymap_image_editor = None
keymap_items_image_editor_1 = None
keymap_items_image_editor_2 = None
mouse_window_x = 0
mouse_window_y = 0
mouse_region_x = 0
mouse_region_y = 0


# EXTEND PATH
# +++++++++++++++++++++++++++++++++++++++
Expand Down Expand Up @@ -149,15 +166,18 @@ def check_dependecies(cls, debug=False):
@classmethod
def unload_dependecies(cls):

# are all modules in the packages list available in the "lib" directory?
for module in cls.external_dependecies:
# if the addon is not in installer mode
if not LookingGlassAddon.external_dependecies_installer:

# get names
module_name, install_name, install_version = module
# are all modules in the packages list available in the "lib" directory?
for module in cls.external_dependecies:

# unload the module
del sys.modules[module_name]
#del module_name
# get names
module_name, install_name, install_version = module

# unload the module
del sys.modules[module_name]
#del module_name



Expand Down Expand Up @@ -258,9 +278,9 @@ def update_logger_levels(self, context):

# update the lightfield window to display a lightfield on the device
@staticmethod
def update_lightfield_window(render_mode, lightfield_image, flip_views=None, invert=None):
def update_lightfield_window(window_mode, lightfield_image, flip_views=None, invert=None):
''' update the lightfield image that is displayed on the current device '''
''' render_mode = 0: Lightfield Viewport, render_mode = 1: Quilt Viewer, render_mode = -1: demo quilt '''
''' window_mode = 0: Lightfield Viewport, window_mode = 1: Quilt Viewer, window_mode = -1: demo quilt '''

# append the add-on's path to Blender's python PATH
sys.path.insert(0, LookingGlassAddon.path)
Expand All @@ -273,36 +293,39 @@ def update_lightfield_window(render_mode, lightfield_image, flip_views=None, inv

# update the variable for the current Looking Glass device
device = pylio.DeviceManager.get_active()

# if a valid device is connected
if device:

# if a LightfieldImage was given
if lightfield_image:

# VIEWPORT MODE
##################################################################
if render_mode == 0:
if window_mode == 0:

if flip_views is None: flip_views = False
if invert is None: invert = False

# let the device display the image
device.display(lightfield_image, flip_views=flip_views, invert=invert)
if device.service: device.display(lightfield_image, flip_views=flip_views, invert=invert)

# QUILT VIEWER MODE
##################################################################
# if the quilt view mode is active AND an image is loaded
elif render_mode == 1:
elif window_mode == 1:

if flip_views is None: flip_views = True
if invert is None: invert = False

# let the device display the image
device.display(lightfield_image, flip_views=flip_views, invert=invert)
if device.service: device.display(lightfield_image, flip_views=flip_views, invert=invert)

# if the demo quilt was requested
elif lightfield_image is None:

# let the device display the demo quilt
device.display(None)
if device.service: device.display(None)

else:
LookingGlassAddonLogger.error("Could not update the lightfield window. No LightfieldImage was given.")
3 changes: 3 additions & 0 deletions lib/pylightio/lookingglass/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ def calibration(self, value):

@property
def defaultQuilt(self):
if isinstance(self.configuration['defaultQuilt'], str):
import json
return json.loads(self.configuration['defaultQuilt'])
return self.configuration['defaultQuilt']

@defaultQuilt.setter
Expand Down
Loading

0 comments on commit 67e2839

Please sign in to comment.