pax_global_header 0000666 0000000 0000000 00000000064 12724054714 0014520 g ustar 00root root 0000000 0000000 52 comment=2df81ca6ea2fd4117efa1aafc63b97a35bae2d92
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/ 0000775 0000000 0000000 00000000000 12724054714 0024322 5 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/.gitignore 0000664 0000000 0000000 00000000022 12724054714 0026304 0 ustar 00root root 0000000 0000000 *.swp
__pycache__
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/.gitmodules 0000664 0000000 0000000 00000000225 12724054714 0026476 0 ustar 00root root 0000000 0000000 [submodule "space_view3d_virtual_reality/lib/hmd_sdk_bridge"]
path = space_view3d_virtual_reality/lib/hmd_sdk_bridge
url = ../hmd_sdk_bridge_build
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/README.md 0000664 0000000 0000000 00000005417 12724054714 0025610 0 ustar 00root root 0000000 0000000 # Virtual Reality Viewport
Addon to bring virtual reality devices to the Blender viewport.
This is work in progress, use at your own risk.
Pre-Requisite
============
* Blender 2.77 (https://builder.blender.org/download)
Oculus Legacy (Linux, Mac, Windows)
* Oculus 0.5 runtime
Oculus (Windows)
* Oculus 0.7 runtime
How to Use
==========
In the ``User Preferences``, ``Add-ons``, ``3D View: Virtual Reality Viewport`` select the Display Backend you want to use.
In the viewport go to the toolshelf, select the ``Virtual Reality`` tab, click on the ``Virtual Reality`` button and follow the on-screen instructions.
Current State
=============
Video of an old version of the plugin working:
[![Video of plugin in action](http://img.youtube.com/vi/saSn2qvW0aE/0.jpg)](https://www.youtube.com/watch?v=saSn2qvW0aE)
Easy Installation
=================
You can get the latest version of the Addon here:
http://www.dalaifelinto.com/ftp/builds/space_view3d_virtual_reality.zip
Advanced Installation
=====================
In a terminal paste the following commands:
```
$ git clone https://github.com/dfelinto/virtual_reality_viewport.git
$ cd virtual_reality_viewport
$ git submodule update --init --recursive --remote
$ zip -x __pycache__ -x */.git* -r9 space_view3d_virtual_reality.zip space_view3d_virtual_reality
```
Now install the space_view3d_virtual_reality.zip in Blender as an addon.
Update
======
In a terminal paste the following commands:
```
$ git pull origin master
$ git submodule update --recursive --remote
```
Followed by the rsync command for your OS:
Mac:
```
$ rsync -rv --exclude=.DS_Store --exclude=.git --exclude=*.blend1 --exclude=*.blend2 --exclude=*.swp --exclude=*.swo space_view3d_virtual_reality ~/Library/Application\ Support/Blender/2.76/scripts/addons/
```
Linux:
```
$ rsync -rv --exclude=.DS_Store --exclude=.git --exclude=*.blend1 --exclude=*.blend2 --exclude=*.swp --exclude=*.swo space_view3d_virtual_reality ~/.config/blender/2.76/scripts/addons/
```
Optionally, instead of rsync you can generate a new ``.zip``, remove the previous version of the addon and re-install it.
Roadmap
=======
* Upgrade Oculus SDK 0.7 to 1.3
* Extend the external bridge library to support other HMD devices
Credits
=======
* Oculus SDK 0.5 wrapper by https://github.com/jherico/python-ovrsdk
* Oculus SDK 0.7 bridge: Dalai Felinto and Djalma Lucio @ Visgraf / IMPA
* Blender Addon - Dalai Felinto - http://www.dalaifelinto.com
Acknowledgements
================
* Visgraf / IMPA - for supporting the core of the addon development
* Djalma Lucio for peer review and discussions about the bridge implementation
* Campbell Barton - for reviewing and contributing to the patches for Blender core
* Thanks for all the testers
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/ 0000775 0000000 0000000 00000000000 12724054714 0032175 5 ustar 00root root 0000000 0000000 __init__.py 0000664 0000000 0000000 00000004602 12724054714 0034231 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality #====================== BEGIN GPL LICENSE BLOCK ======================
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#======================= END GPL LICENSE BLOCK ========================
#
bl_info = {
"name": "Virtual Reality Viewport",
"author": "Dalai Felinto, Visgraf/IMPA",
"version": (0, 9),
"blender": (2, 7, 7),
"location": "View 3D Tools",
"description": "",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "3D View"}
import bpy
from . import ui
from . import operator
# ############################################################
# User Preferences
# ############################################################
# Preferences
class VirtualRealityPreferences(bpy.types.AddonPreferences):
bl_idname = __name__
display_backend = bpy.props.EnumProperty(
name="Display Backend",
description="Library to use for the display",
items=(("OCULUS", "Oculus", "Oculus - oculus.com"),
("OCULUS_LEGACY", "Oculus Legacy", "Oculus 0.5 - oculus.com"),
("DEBUG", "Debug", "Debug backend - no real HMD"),
),
default="OCULUS_LEGACY",
)
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, "display_backend")
# ############################################################
# Un/Registration
# ############################################################
def register():
bpy.utils.register_class(VirtualRealityPreferences)
operator.register()
ui.register()
def unregister():
bpy.utils.unregister_class(VirtualRealityPreferences)
operator.unregister()
ui.unregister()
if __name__ == '__main__':
register()
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/hmd/ 0000775 0000000 0000000 00000000000 12724054714 0032745 5 ustar 00root root 0000000 0000000 __init__.py 0000664 0000000 0000000 00000020246 12724054714 0035003 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/hmd
TODO = True
from mathutils import (
Matrix,
Quaternion,
)
import gpu
VERBOSE = True
# ############################################################
# Data structs
# ############################################################
def HMD(display_backend, context, error_callback):
"""
return the head mounted display device class
(defined in another file)
:param display_backend: backend engine
:type display_backend: str
:param context: BPY context
:type context: bpy.types.Context
:param error_callback: error handler
:type error_callback: func(message, is_fatal)
"""
from .oculus import Oculus
from .oculus_legacy import OculusLegacy
from .debug import Debug
displays = {
'OCULUS':Oculus,
'OCULUS_LEGACY':OculusLegacy,
'DEBUG':Debug,
}
if display_backend not in displays:
assert False, "Display Backend \"{0}\" not implemented".format(display_backend)
return displays[display_backend](context, error_callback)
# ############################################################
# Base class inherited by HMD devices
# ############################################################
class HMD_Base:
__slots__ = {
"_name",
"_current_eye",
"_error_callback",
"_width",
"_height",
"_projection_matrix",
"_head_transformation",
"_is_direct_mode",
"_eye_pose",
"_offscreen",
"_color_texture",
"_modelview_matrix",
"_near",
"_far",
}
def __init__(self, name, is_direct_mode, context, error_callback):
self._name = name
self._is_direct_mode = is_direct_mode
self._error_callback = error_callback
self._current_eye = 0
self._width = [0, 0]
self._height = [0, 0]
self._projection_matrix = [Matrix.Identity(4), Matrix.Identity(4)]
self._modelview_matrix = [Matrix.Identity(4), Matrix.Identity(4)]
self._color_texture = [0, 0]
self._offscreen = [None, None]
self._eye_orientation_raw = [[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]]
self._eye_position_raw = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
self._scale = self._calculateScale(context)
self._updateViewClipping(context)
@property
def is_direct_mode(self):
return self._is_direct_mode
@property
def width(self):
return self._width[self._current_eye]
@width.setter
def width(self, value):
self._width[self._current_eye] = value
@property
def height(self):
return self._height[self._current_eye]
@height.setter
def height(self, value):
self._height[self._current_eye] = value
@property
def offscreen(self):
return self._offscreen[self._current_eye]
@property
def color_texture(self):
return self._color_texture[self._current_eye]
@property
def projection_matrix(self):
return self._projection_matrix[self._current_eye]
@property
def modelview_matrix(self):
return self._modelview_matrix[self._current_eye]
def setEye(self, eye):
self._current_eye = int(bool(eye))
def init(self):
"""
Initialize device
:return: return True if the device was properly initialized
:rtype: bool
"""
try:
for i in range(2):
self._offscreen[i] = gpu.offscreen.new(self._width[i], self._height[i], 0)
if hasattr(self._offscreen[i], "color_texture"):
self._color_texture[i] = self._offscreen[i].color_texture
else: # TODO remove this once the patch is merged
self._color_texture[i] = self._offscreen[i].color_object
except Exception as E:
print(E)
self._offscreen[0] = None
self._offscreen[1] = None
return False
else:
return True
def loop(self, context):
"""
Get fresh tracking data
"""
self._updateViewClipping(context)
self.updateMatrices(context)
def frameReady(self):
"""
The frame is ready to be sent to the device
"""
assert False, "frameReady() not implemented for the \"{0}\" device".format(self._name)
def reCenter(self):
"""
Re-center the HMD device
:return: return True if success
:rtype: bool
"""
assert False, "reCenter() not implemented for the \"{0}\" device".format(self._name)
def quit(self):
"""
Garbage collection
"""
try:
for i in range(2):
self._offscreen[i] = None
except Exception as E:
print(E)
def error(self, function, exception, is_fatal):
"""
Handle error messages
"""
if VERBOSE:
print("ADD-ON :: {0}() : {1}".format(function, exception))
import sys
traceback = sys.exc_info()
if traceback and traceback[0]:
print(traceback[0])
if hasattr(exception, "strerror"):
message = exception.strerror
else:
message = str(exception)
# send the error the interface
self._error_callback(message, is_fatal)
def updateMatrices(self, context):
"""
Update OpenGL drawing matrices
"""
vr = context.window_manager.virtual_reality
tracking_mode = vr.tracking_mode
view_matrix = self._getViewMatrix(context, vr.lock_camera)
for i in range(2):
if tracking_mode == 'NONE':
self._modelview_matrix[i] = view_matrix
continue
rotation_raw = self._eye_orientation_raw[i]
rotation = Quaternion(rotation_raw).to_matrix().to_4x4()
if tracking_mode == 'ALL':
position_raw = self._eye_position_raw[i]
# take scene units into consideration
position_raw = self._scaleMovement(position_raw)
position = Matrix.Translation(position_raw)
transformation = position * rotation
else: # 'ROTATION'
# rotation only, ignore the positional data
transformation = rotation
self._modelview_matrix[i] = transformation.inverted() * view_matrix
def _getViewMatrix(self, context, lock_camera):
region = context.region_data
if (self._is_direct_mode and lock_camera) or (region.view_perspective == 'CAMERA'):
space = context.space_data
camera = space.camera
return camera.matrix_world.inverted()
else:
return region.view_matrix.copy()
def _updateViewClipping(self, context):
space = context.space_data
region = context.region_data
if region.view_perspective == 'CAMERA':
camera_ob = space.camera
camera = camera_ob.data
self._near = camera.clip_start
self._far = camera.clip_end
else:
self._near = space.clip_start
self._far = space.clip_end
def _calculateScale(self, context):
"""
if BU != 1 meter, scale the transformations
"""
scene = context.scene
unit_settings = scene.unit_settings
system = unit_settings.system
if system == 'NONE':
return None
elif system == 'METRIC':
return 1.0 / unit_settings.scale_length
elif system == 'IMPERIAL':
return 0.3048 / unit_settings.scale_length
else:
assert('Unit system not supported ({0})'.format(system))
def _scaleMovement(self, position):
"""
if BU != 1 meter, scale the transformations
"""
if self._scale is None:
return position
return [position[0] * self._scale,
position[1] * self._scale,
position[2] * self._scale]
def _convertMatrixTo4x4(self, value):
matrix = Matrix()
matrix[0] = value[0:4]
matrix[1] = value[4:8]
matrix[2] = value[8:12]
matrix[3] = value[12:16]
return matrix.transposed()
debug.py 0000664 0000000 0000000 00000004176 12724054714 0034336 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/hmd """
Debug
=====
Debug device for testing
"""
from . import HMD_Base
VERBOSE = False
def print_debug(*args):
if VERBOSE:
print("Debug: {0}".format(*args))
class Debug(HMD_Base):
def __init__(self, context, error_callback):
super(Debug, self).__init__('Debug', False, context, error_callback)
def init(self, context):
"""
Initialize device
:return: return True if the device was properly initialized
:rtype: bool
"""
print_debug('init()')
self._width = [512, 512]
self._height = [512, 512]
return super(Debug, self).init()
def loop(self, context):
"""
Get fresh tracking data
"""
print_debug('loop()')
from math import fmod, radians
from mathutils import Matrix
global time
speed = 0.001
_range = 45.0
time = fmod(time + speed, 1.0)
factor = time * 2.0
if factor > 1.0:
factor = 2.0 - factor
one = 1.0 - factor
# one goes from 0.0 to 1.0, and then from 1.0 to 0.0
# angle goes from - range * 0.5 to + range * 0.5
angle = (one * _range) - (_range * 0.5)
quaternion = list(Matrix.Rotation(radians(angle), 4, 'Y').to_quaternion())
projection_matrix = self._getProjectionMatrix(context)
for eye in range(2):
self._eye_orientation_raw[eye] = quaternion
self._projection_matrix[eye] = projection_matrix
super(Debug, self).loop(context)
def _getProjectionMatrix(self, context):
region = context.region_data
if region.view_perspective == 'CAMERA':
space = context.space_data
camera = space.camera
return camera.calc_matrix_camera()
else:
return region.perspective_matrix.copy()
def frameReady(self):
"""
The frame is ready to be send to the device
"""
print_debug('frameReady()')
def quit(self):
"""
Garbage collection
"""
print_debug('quit()')
return super(Debug, self).quit()
global time
time = 0.0
oculus.py 0000664 0000000 0000000 00000006270 12724054714 0034557 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/hmd """
Oculus
======
Oculus (oculus.com) head mounted display
It uses a C app to connect with the SDK
The bridge code is hosted at Visgraf:
http://git.impa.br/dfelinto/hmd_sdk_bridge
"""
from . import HMD_Base
from ..lib import (
checkModule,
)
class Oculus(HMD_Base):
def __init__(self, context, error_callback):
super(Oculus, self).__init__('Oculus', True, context, error_callback)
checkModule('hmd_sdk_bridge')
def _getHMDClass(self):
from bridge.hmd.oculus import HMD
return HMD
@property
def projection_matrix(self):
if self._current_eye:
matrix = self._hmd.getProjectionMatrixRight(self._near, self._far)
else:
matrix = self._hmd.getProjectionMatrixLeft(self._near, self._far)
self.projection_matrix = matrix
return super(Oculus, self).projection_matrix
@projection_matrix.setter
def projection_matrix(self, value):
self._projection_matrix[self._current_eye] = \
self._convertMatrixTo4x4(value)
def init(self, context):
"""
Initialize device
:return: return True if the device was properly initialized
:rtype: bool
"""
try:
HMD = self._getHMDClass()
self._hmd = HMD()
# gather arguments from HMD
self.setEye(0)
self.width = self._hmd.width_left
self.height = self._hmd.height_left
self.setEye(1)
self.width = self._hmd.width_right
self.height = self._hmd.height_right
# initialize FBO
if not super(Oculus, self).init():
raise Exception("Failed to initialize HMD")
# send it back to HMD
if not self._setup():
raise Exception("Failed to setup Oculus")
except Exception as E:
self.error("init", E, True)
self._hmd = None
return False
else:
return True
def _setup(self):
return self._hmd.setup(self._color_texture[0], self._color_texture[1])
def loop(self, context):
"""
Get fresh tracking data
"""
try:
data = self._hmd.update()
self._eye_orientation_raw[0] = data[0]
self._eye_orientation_raw[1] = data[2]
self._eye_position_raw[0] = data[1]
self._eye_position_raw[1] = data[3]
# update matrices
super(Oculus, self).loop(context)
except Exception as E:
self.error("loop", E, False)
return False
return True
def frameReady(self):
"""
The frame is ready to be sent to the device
"""
try:
self._hmd.frameReady()
except Exception as E:
self.error("frameReady", E, False)
return False
return True
def reCenter(self):
"""
Re-center the HMD device
:return: return True if success
:rtype: bool
"""
return self._hmd.reCenter()
def quit(self):
"""
Garbage collection
"""
self._hmd = None
return super(Oculus, self).quit()
oculus_legacy.py 0000664 0000000 0000000 00000001037 12724054714 0036077 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/hmd """
Oculus Legacy
=============
Oculus (oculus.com) head mounted display for OSX and Linux
It uses a python wrapper to connect with the SDK
"""
from . import HMD_Base
from .oculus import Oculus
from ..lib import (
checkModule,
)
class OculusLegacy(Oculus):
def __init__(self, context, error_callback):
HMD_Base.__init__(self, 'Oculus Legacy', False, context, error_callback)
checkModule('hmd_sdk_bridge')
def _getHMDClass(self):
from bridge.hmd.oculus_legacy import HMD
return HMD
lib.py 0000664 0000000 0000000 00000001376 12724054714 0033245 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality """
Library
=======
Sorted util functions
"""
def getAddonName():
return __name__.split('.')[0]
def getDisplayBackend(context):
"""
Preference set in the addon
"""
addon = getAddonName()
preferences = context.user_preferences.addons[addon].preferences
return preferences.display_backend
def checkModule(path):
"""
If library exists append it to sys.path
"""
import sys
import os
addon_path = os.path.dirname(os.path.abspath(__file__))
library_path = os.path.join(addon_path, "lib", path)
if library_path not in sys.path:
sys.path.append(library_path)
def isMac():
"""
Return True if OS is Mac OSX
"""
from sys import platform as _platform
return _platform == "darwin"
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/lib/ 0000775 0000000 0000000 00000000000 12724054714 0032743 5 ustar 00root root 0000000 0000000 hmd_sdk_bridge/ 0000775 0000000 0000000 00000000000 12724054714 0035611 5 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/lib opengl_helper.py 0000664 0000000 0000000 00000027164 12724054714 0035325 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality import bpy
from bgl import *
from mathutils import Matrix, Euler
SpaceView3D = bpy.types.SpaceView3D
callback_handle = []
fragment_shader ="""
#version 120
uniform sampler2D color_buffer;
void main(void)
{
vec2 coords = gl_TexCoord[0].st;
vec4 foreground = texture2D(color_buffer, coords);
gl_FragColor = mix(foreground, vec4(1.0, 0.0, 0.0, 1.0), 0.5);
}
"""
# ##################
# GLSL Debug
# ##################
def print_shader_errors(shader):
""""""
log = Buffer(GL_BYTE, len(fragment_shader))
length = Buffer(GL_INT, 1)
print('Shader Code:')
glGetShaderSource(shader, len(log), length, log)
line = 1
msg = " 1 "
for i in range(length[0]):
if chr(log[i-1]) == '\n':
line += 1
msg += "%3d %s" %(line, chr(log[i]))
else:
msg += chr(log[i])
print(msg)
glGetShaderInfoLog(shader, len(log), length, log)
print("Error in GLSL Shader:\n")
msg = ""
for i in range(length[0]):
msg += chr(log[i])
print (msg)
def print_program_errors(program):
""""""
log = Buffer(GL_BYTE, 1024)
length = Buffer(GL_INT, 1)
glGetProgramInfoLog(program, len(log), length, log)
print("Error in GLSL Program:\n")
msg = ""
for i in range(length[0]):
msg += chr(log[i])
print (msg)
# ######################
# OpenGL Image Routines
# ######################
def resize(self, context):
"""we can run every frame or only when width/height change"""
# remove old textures
self.quit()
self.width = context.region.width
self.height = context.region.height
self.buffer_width, self.buffer_height = calculate_image_size(self.width, self.height)
# image to dump screen
self.color_id = create_image(self.buffer_width, self.buffer_height, GL_RGBA)
def calculate_image_size(width, height):
"""get a power of 2 size"""
buffer_width, buffer_height = 0,0
i = 0
while (1 << i) <= width:i+= 1
buffer_width = 1 << i
i = 0
while (1 << i) <= height:i+= 1
buffer_height = 1 << i
return buffer_width, buffer_height
def update_image(tex_id, viewport, target=GL_RGBA, texture=GL_TEXTURE0):
"""copy the current buffer to the image"""
glActiveTexture(texture)
glBindTexture(GL_TEXTURE_2D, tex_id)
glCopyTexImage2D(GL_TEXTURE_2D, 0, target, viewport[0], viewport[1], viewport[2], viewport[3], 0)
glBindTexture(GL_TEXTURE_2D, 0)
def create_image(width, height, target=GL_RGBA):
"""create an empty image, dimensions pow2"""
if target == GL_RGBA:
target, internal_format, dimension = GL_RGBA, GL_RGB, 3
else:
target, internal_format, dimension = GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, 1
null_buffer = Buffer(GL_BYTE, [(width + 1) * (height + 1) * dimension])
id_buf = Buffer(GL_INT, 1)
glGenTextures(1, id_buf)
tex_id = id_buf.to_list()[0]
glBindTexture(GL_TEXTURE_2D, tex_id)
glTexImage2D(GL_TEXTURE_2D, 0, target, width, height, 0, internal_format, GL_UNSIGNED_BYTE, null_buffer)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
if target == GL_DEPTH_COMPONENT32:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE)
glCopyTexImage2D(GL_TEXTURE_2D, 0, target, 0, 0, width, height, 0)
glBindTexture(GL_TEXTURE_2D, 0)
del null_buffer
return tex_id
def delete_image(tex_id):
"""clear created image"""
id_buf = Buffer(GL_INT, 1)
id_buf.to_list()[0] = tex_id
if glIsTexture(tex_id):
glDeleteTextures(1, id_buf)
# ##################
# Framebuffer Routines
# ##################
def check_framebuffer_status(target):
status = glCheckFramebufferStatus(target)
if status == GL_FRAMEBUFFER_COMPLETE:
return True
elif status == GL_FRAMEBUFFER_UNDEFINED:
print("framebuffer not complete: GL_FRAMEBUFFER_UNDEFINED - returned if the specified framebuffer is the default read or draw framebuffer, but the default framebuffer does not exist.")
elif status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
print("framebuffer not complete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT - returned if any of the framebuffer attachment points are framebuffer incomplete.")
elif status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
print("framebuffer not complete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT - returned if the framebuffer does not have at least one image attached to it.")
elif status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
print("framebuffer not complete: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER - returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point named by GL_DRAW_BUFFERi.")
elif status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
print("framebuffer not complete: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER - returned if GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER.")
elif status == GL_FRAMEBUFFER_UNSUPPORTED:
print("framebuffer not complete: GL_FRAMEBUFFER_UNSUPPORTED - returned if the combination of internal formats of the attached images violates an implementation-dependent set of restrictions.")
elif status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
print("framebuffer not complete: GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE - returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES. also returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS i s not the same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached textures.")
elif status == GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
print("framebuffer not complete: GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS - returned if any framebuffer attachment is layered, and any populated attachment is not layered, or if all populated color attachments are not from textures of the same target.")
else:
print("framebuffer not complete: status 0x%x (unknown)" % (status,))
return False
def create_framebuffer(width, height, target=GL_RGBA):
"""create an empty framebuffer"""
id_buf = Buffer(GL_INT, 1)
glGenFramebuffers(1, id_buf)
fbo_id = id_buf.to_list()[0]
if fbo_id == 0:
print("Framebuffer error on creation")
return -1
tex_id = create_image(width, height)
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0)
glGenRenderbuffers(1, id_buf)
depth_id = id_buf.to_list()[0]
glBindRenderbuffer(GL_RENDERBUFFER, depth_id)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, width, height)
# attach the depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_id)
#glDrawBuffers(fbo_id, GL_COLOR_ATTACHMENT0)
if not check_framebuffer_status(GL_DRAW_FRAMEBUFFER):
delete_framebuffer(fbo_id)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
return -1
glBindFramebuffer(GL_FRAMEBUFFER, 0)
return fbo_id
def delete_framebuffer(fbo_id):
"""clear created framebuffer"""
id_buf = Buffer(GL_INT, 1)
id_buf.to_list()[0] = fbo_id
if glIsFramebuffer(fbo_id):
glDeleteFramebuffers(1, id_buf)
# ##################
# GLSL Screen Shader
# ##################
def create_shader(source, program=None, type=GL_FRAGMENT_SHADER):
""""""
if program == None:
program = glCreateProgram()
shader = glCreateShader(type)
glShaderSource(shader, source)
glCompileShader(shader)
success = Buffer(GL_INT, 1)
glGetShaderiv(shader, GL_COMPILE_STATUS, success)
if not success[0]:
print_shader_errors(shader)
glAttachShader(program, shader)
glLinkProgram(program)
return program
def setup_uniforms(program, color_id, width, height, is_left):
""""""
uniform = glGetUniformLocation(program, "bgl_RenderedTexture")
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, color_id)
if uniform != -1: glUniform1i(uniform, 0)
uniform = glGetUniformLocation(program, "bgl_RenderedTextureWidth")
if uniform != -1: glUniform1f(uniform, width)
uniform = glGetUniformLocation(program, "bgl_RenderedTextureHeight")
if uniform != -1: glUniform1f(uniform, height)
uniform = glGetUniformLocation(program, "bgl_RenderedStereoEye")
if uniform != -1: glUniform1i(uniform, 0 if is_left else 1)
def bindcode(image):
"""load the image in the graphic card if necessary"""
image.gl_touch(GL_NEAREST)
return image.bindcode
# ##################
# Drawing Routines
# ##################
def view_setup():
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glMatrixMode(GL_TEXTURE)
glPushMatrix()
glLoadIdentity()
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
glOrtho(-1, 1, -1, 1, -20, 20)
gluLookAt(0.0, 0.0, 1.0, 0.0,0.0,0.0, 0.0,1.0,0.0)
def view_reset():
# Get texture info
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_TEXTURE)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
def draw_rectangle_rainbow(zed=0.0):
texco = [(1, 1), (0, 1), (0, 0), (1,0)]
verco = [(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), ( 1.0, -1.0)]
colors = [(1.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (1.0, 1.0, 0.0, 0.0)]
glPolygonMode(GL_FRONT_AND_BACK , GL_FILL)
glBegin(GL_QUADS)
for i in range(4):
color = colors[i]
glColor4f(color[0], color[1], color[2], color[3])
glTexCoord3f(texco[i][0], texco[i][1], zed)
glVertex2f(verco[i][0], verco[i][1])
glEnd()
def draw_rectangle(zed=0.0):
texco = [(1, 1), (0, 1), (0, 0), (1,0)]
verco = [(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), ( 1.0, -1.0)]
glPolygonMode(GL_FRONT_AND_BACK , GL_FILL)
glColor4f(1.0, 1.0, 1.0, 0.0)
glBegin(GL_QUADS)
for i in range(4):
glTexCoord3f(texco[i][0], texco[i][1], zed)
glVertex2f(verco[i][0], verco[i][1])
glEnd()
def draw_callback_px(self, context):
"""core function"""
if not self._enabled: return
is_left = self.is_stereo_left(context)
act_tex = Buffer(GL_INT, 1)
glGetIntegerv(GL_ACTIVE_TEXTURE, act_tex)
glGetIntegerv(GL_VIEWPORT, self.viewport)
# (1) dump buffer in texture
update_image(self.color_id, self.viewport, GL_RGBA, GL_TEXTURE0)
# (2) run screenshader
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LESS)
pjm = Buffer(GL_FLOAT, 16)
mvm = Buffer(GL_FLOAT, 16)
cam_pos = context.scene.camera.location.copy()
glMatrixMode(GL_MODELVIEW)
glTranslatef(cam_pos[0], cam_pos[1], cam_pos[2])
glGetFloatv(GL_PROJECTION_MATRIX, pjm)
glGetFloatv(GL_MODELVIEW_MATRIX, mvm)
# set identity matrices
view_setup()
# update shader
glUseProgram(self.program_shader)
setup_uniforms(self.program_shader, self.color_id, self.width, self.height, is_left)
draw_rectangle()
# (3) restore opengl defaults
glUseProgram(0)
glActiveTexture(act_tex[0])
glBindTexture(GL_TEXTURE_2D, 0)
view_reset()
glViewport(self.viewport[0], self.viewport[1], self.viewport[2], self.viewport[3])
glMatrixMode(GL_MODELVIEW)
glTranslatef(-cam_pos[0], -cam_pos[1], -cam_pos[2])
operator.py 0000664 0000000 0000000 00000053600 12724054714 0034327 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality import bpy
from bpy.app.handlers import persistent
from .hmd import HMD
from .preview import Preview
from .lib import (
getDisplayBackend,
isMac,
)
TODO = False
# ############################################################
# Commands
# ############################################################
class Commands:
recenter = 'RECENTER'
fullscreen = 'FULLSCREEN'
play = 'PLAY'
pause = 'PAUSE'
test = 'TEST'
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
ready = 5 # all went well
play = 6 # play
pause = 7 # pause
paused = 8 # paused
error = 9 # 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 = ""
# update the values in def _init_static
_hmd = None
_timer = None
_handle_pre = None
_handle_post = None
_handle_pixel = None
_hash_slave = -1
_hash_master = -1
_slave_status = 0
_slave_window = None
_slave_area = None
_is_mac = False
_visible_master = None
_visible_slave = None
_is_rendering = False
action = bpy.props.EnumProperty(
description="",
items=(("ENABLE", "Enable", "Enable"),
("DISABLE", "Disable", "Disable"),
("TOGGLE", "Toggle", "Toggle"),
("RECENTER", "Re-Center", "Re-Center tracking data"),
("FULLSCREEN", "Fullscreen", "Make slave fullscreen"),
("PLAY", "Play", ""),
("PAUSE", "Pause", ""),
),
default="TOGGLE",
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'}
if not vr.is_enabled:
self._quit(context)
area.tag_redraw()
return {'FINISHED'}
if event.type == 'TIMER' and \
not vr.is_paused:
if self._slave_area:
self._slave_area.tag_redraw()
if self._hmd and self._hmd.is_direct_mode:
self._drawMaster(context)
if vr.use_preview:
area.tag_redraw()
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'
if self.action == 'DISABLE':
if is_enabled:
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
self._quit(context)
elif self.action == 'RECENTER':
vr.command_push(Commands.recenter)
return {'FINISHED'}
elif self.action == 'FULLSCREEN':
vr.command_push(Commands.fullscreen)
return {'FINISHED'}
elif self.action == 'PLAY':
vr.command_push(Commands.play)
# we define is_paused right away, so
# the next MODAL loop already tag_redraw
vr.is_paused = False
return {'FINISHED'}
elif self.action == 'PAUSE':
vr.command_push(Commands.pause)
self._redraw(context)
return {'FINISHED'}
else:
assert False, "action \"{0}\" not implemented".format(self.action)
return {'CANCELLED'}
def _redraw(self, context, redraw_master=True, redraw_slave=True):
if redraw_slave and self._slave_area:
self._slave_area.tag_redraw()
if redraw_master:
context.area.tag_redraw()
def quit(self, context):
"""garbage collect"""
# change it so the original modal operator will clean things up
wm = context.window_manager
wm.virtual_reality.reset()
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_pre:
bpy.types.SpaceView3D.draw_handler_remove(self._handle_pre, 'WINDOW')
self._handle_pre = None
if self._handle_post:
bpy.types.SpaceView3D.draw_handler_remove(self._handle_post, 'WINDOW')
self._handle_post = None
if self._handle_pixel:
bpy.types.SpaceView3D.draw_handler_remove(self._handle_pixel, 'WINDOW')
self._handle_pixel = None
self._preview.quit()
if self._hmd:
self._hmd.quit()
if self._slave_window:
override = context.copy()
override['window'] = self._slave_window
self._slave_window = None
bpy.ops.wm.window_close(override)
# cleanup viewport
if context.area:
context.area.tag_redraw()
def _init_static(self):
self._hmd = None
self._timer = None
self._handle_pre = None
self._handle_post = None
self._handle_pixel = None
self._hash_slave = -1
self._hash_master = -1
self._slave_status = 0
self._slave_window = None
self._slave_area = None
self._is_mac = isMac()
self._visible_master = None
self._visible_slave = None
self._is_rendering = False
def init(self, context):
"""
Initialize the callbacks and the external devices
"""
wm = context.window_manager
vr = wm.virtual_reality
vr.reset()
vr.is_enabled = True
self._init_static()
display_backend = getDisplayBackend(context)
self._hmd = HMD(display_backend, context, self._error_callback)
self._preview = Preview()
self._hash_master = hash(context.area)
# setup modal
self._timer = wm.event_timer_add(1.0 / 75.0, context.window) # 75 Hz
self._handle_pre = bpy.types.SpaceView3D.draw_handler_add(self._draw_callback_pre, (context,), 'WINDOW', 'PRE_VIEW')
self._handle_post = bpy.types.SpaceView3D.draw_handler_add(self._draw_callback_post, (context,), 'WINDOW', 'POST_VIEW')
self._handle_pixel = bpy.types.SpaceView3D.draw_handler_add(self._draw_callback_pixel, (context,), 'WINDOW', 'POST_PIXEL')
wm.modal_handler_add(self)
if self._hmd.is_direct_mode:
self._init(context)
else:
vr.is_slave_setup = True
return self._slaveSetup(context)
return True
def _init(self, context):
if not self._hmd.init(context):
self.report({'ERROR'}, "Error initializing device")
return False
# get the data from device
color_texture = [0, 0]
for i in range(2):
self._hmd.setEye(i)
color_texture[i] = self._hmd.color_texture
self._preview.init(color_texture[0], color_texture[1])
return True
def _slaveSetup(self, context):
ok = True
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:
if not self._is_mac:
bpy.ops.wm.window_fullscreen_toggle()
space = context.space_data
if space.camera:
region = context.region_data
region.view_perspective = 'CAMERA'
context.window_manager.virtual_reality.is_slave_setup = False
ok = self._init(context)
self._slave_status = SlaveStatus.ready
elif self._slave_status == SlaveStatus.play:
context.window_manager.virtual_reality.is_paused = False
self._slave_status = SlaveStatus.ready
elif self._slave_status == SlaveStatus.pause:
context.window_manager.virtual_reality.is_paused = True
context.area.tag_redraw()
self._slave_status = SlaveStatus.paused
else:
assert False, "_slaveSetup: Slave status \"{0}\" not defined".format(self._slave_status)
if not ok:
self._slave_status = SlaveStatus.error
self.quit(context)
self._slave_window = context.window
return ok
def _slaveHook(self, context, mode=''):
self._hash_slave = -1
self._slave_area = None
self._slave_status = SlaveStatus.non_setup
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
self._slave_area = area
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.play:
self._slave_status = SlaveStatus.play
self._slaveSetup(context)
elif command == Commands.pause:
self._slave_status = SlaveStatus.pause
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
"""
self._is_rendering = True
self._hmd.loop(context)
scene = context.scene
view3d = context.space_data
region = context.region
for i in range(2):
self._hmd.setEye(i)
offscreen = self._hmd.offscreen
projection_matrix = self._hmd.projection_matrix
modelview_matrix = self._hmd.modelview_matrix
# drawing
offscreen.draw_view3d(scene, view3d, region, projection_matrix, modelview_matrix)
self._hmd.frameReady()
self._is_rendering = False
def _drawPreview(self, context):
wm = context.window_manager
vr = wm.virtual_reality
if vr.use_preview:
self._preview.loop(vr.preview_scale)
def _drawMaster(self, context):
wm = context.window_manager
vr = wm.virtual_reality
if self._hmd.is_direct_mode:
self._commands(context)
if vr.is_paused:
return
if self._hmd.is_direct_mode:
self._loop(context)
else:
self._drawPreview(context)
def _drawSlave(self, context):
wm = context.window_manager
vr = wm.virtual_reality
if self._hmd.is_direct_mode:
return
self._commands(context)
if vr.is_paused:
return
if self._slave_status == SlaveStatus.ready:
self._loop(context)
elif self._slave_status == SlaveStatus.paused:
return
elif self._slave_status == SlaveStatus.waituser:
self._drawDisplayMessage(context)
else:
self._slaveSetup(context)
def _drawDisplayMessage(self, context):
"""
Message telling user to move the window the HMD display
"""
from bgl import glColor4f
window = context.window
width = window.width
height = window.height
glColor4f(1.0, 1.0, 1.0, 1.0)
font_id = 0
# draw some text
x = int(0.1 * width)
y = int(0.5 * height)
font_size = int(width * 0.035)
line_gap = int(font_size * 1.5)
from blf import (
SHADOW,
enable,
shadow,
shadow_offset,
position,
size,
draw,
disable,
)
enable(font_id, SHADOW)
shadow(font_id, 5, 0.0, 0.0, 0.0, 1.0)
shadow_offset(font_id, -2, -2)
size(font_id, font_size, 72)
if self._is_mac:
position(font_id, x, y + line_gap, 0)
draw(font_id, "1. Move this window to the external HMD display")
position(font_id, x, y, 0)
draw(font_id, "2. Set this window to fullscreen (Alt + F11)")
position(font_id, x, y - line_gap, 0)
draw(font_id, "3. Select \"Start\" in the main window")
else:
position(font_id, x, y, 0)
draw(font_id, "1. Move this window to the external HMD display")
position(font_id, x, y - line_gap, 0)
draw(font_id, "2. Select \"Start\" in the main window")
disable(font_id, SHADOW)
def _pre_draw_hide(self, context, visible):
scene = context.scene
space = context.space_data
visible['objects'] = []
objects = visible['objects']
for ob in scene.objects:
if not ob.hide:
objects.append(ob)
ob.hide = True
visible['show_grease_pencil'] = space.show_grease_pencil
space.show_grease_pencil = False
def _post_draw_show(self, context, visible):
space = context.space_data
objects = visible['objects']
for ob in objects:
ob.hide = False
visible['objects'] = []
space.show_grease_pencil = visible['show_grease_pencil']
def _hide_master(self, context):
"""
whether to hide the main 3d viewport
"""
vr = context.window_manager.virtual_reality
if vr.is_paused:
return False
if vr.use_hmd_only:
return True
if vr.use_preview and vr.preview_scale == 100:
return True
def _draw_callback_pre(self, context):
"""
hide all the scene objects to speed up rendering
"""
if self._is_rendering:
return
area = context.area
hash_area = hash(area)
if hash_area == self._hash_slave:
self._visible_slave = {}
self._pre_draw_hide(context, self._visible_slave)
elif hash_area == self._hash_master and self._hide_master(context):
self._visible_master = {}
self._pre_draw_hide(context, self._visible_master)
def _draw_callback_post(self, context):
"""
show all the hidden objects
"""
if self._is_rendering:
return
area = context.area
hash_area = hash(area)
if hash_area == self._hash_slave:
self._post_draw_show(context, self._visible_slave)
elif hash_area == self._hash_master and self._hide_master(context):
self._post_draw_show(context, self._visible_master)
def _draw_callback_pixel(self, context):
"""
callback function, run every time the viewport is refreshed
"""
if self._is_rendering:
return
area = context.area
hash_area = hash(area)
if hash_area == self._hash_slave:
self._drawSlave(context)
elif hash_area == self._hash_master:
if self._hmd and self._hmd.is_direct_mode:
self._drawPreview(context)
else:
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.fullscreen, "Fullscreen", ""),
(Commands.play, "Play", ""),
(Commands.pause, "Pause", ""),
(Commands.test, "Test", ""),
),
default="NONE",
)
class VirtualRealityInfo(bpy.types.PropertyGroup):
is_enabled = BoolProperty(
name="Enabled",
default=False,
)
is_paused = BoolProperty(
name="Paused",
default=False,
)
use_preview = BoolProperty(
name="Preview",
default=False,
)
use_hmd_only = BoolProperty(
name="HMD Only",
default=False,
)
preview_scale = IntProperty(
name="Preview Scale",
min=0,
max=100,
default=20,
subtype='PERCENTAGE',
)
error_message = StringProperty(
name="Error Message",
)
tracking_mode = EnumProperty(
name="Tracking Mode",
description="",
items=(("ALL", "All", ""),
("ROTATION", "Rotation Only", "Ignore positional tracking data"),
("NONE", "None", "No tracking"),
),
default="ALL",
)
lock_camera = BoolProperty(
name="Lock Camera",
description="Lock the view to the camera (only for Direct Mode)",
default=False,
)
is_slave_setup = BoolProperty(
default = False,
)
is_debug = BoolProperty(
name = "Debug",
default = False,
description = "Skip the optimization to prevent extra drawing",
)
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
def reset(self):
while self.commands:
self.commands.remove(0)
self.use_preview = False
self.use_hmd_only = False
self.error_message = ""
self.is_enabled = False
self.is_slave_setup = False
self.is_paused = False
self.is_debug = False
# ############################################################
# Callbacks
# ############################################################
@persistent
def virtual_reality_load_pre(dummy):
wm = bpy.context.window_manager
wm.virtual_reality.reset()
@persistent
def virtual_reality_load_post(dummy):
wm = bpy.context.window_manager
wm.virtual_reality.reset()
# ############################################################
# 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)
preview.py 0000664 0000000 0000000 00000006063 12724054714 0034156 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality """
Viewport Preview Drawing
************************
Routines to draw in the viewport the result
that is projected in the HMD
"""
from .opengl_helper import (
view_reset,
view_setup,
)
from bgl import *
class Preview:
__slots__ = {
"_color_texture_left",
"_color_texture_right",
}
def init(self, color_texture_left, color_texture_right):
"""
Initialize preview window
:param color_texture_left: 2D Texture binding ID (bind to the Framebuffer Object) for left eye
:type color_texture_left: bgl.GLuint
:param color_texture_right: 2D Texture binding ID (bind to the Framebuffer Object) for right eye
:type color_texture_right: bgl.GLuint
"""
self.update(color_texture_left, color_texture_right)
def quit(self):
"""
Destroy preview window
"""
pass
def update(self, color_texture_left, color_texture_right):
"""
Update OpenGL binding textures
:param color_texture_left: 2D Texture binding ID (bind to the Framebuffer Object) for left eye
:type color_texture_left: bgl.GLuint
:param color_texture_right: 2D Texture binding ID (bind to the Framebuffer Object) for right eye
:type color_texture_right: bgl.GLuint
"""
self._color_texture_left = color_texture_left
self._color_texture_right = color_texture_right
def _drawRectangle(self, eye):
texco = [(1, 1), (0, 1), (0, 0), (1,0)]
verco = [[(0.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), ( 0.0, -1.0)],
[(1.0, 1.0), ( 0.0, 1.0), ( 0.0, -1.0), ( 1.0, -1.0)]]
glPolygonMode(GL_FRONT_AND_BACK , GL_FILL)
glColor4f(1.0, 1.0, 1.0, 0.0)
glBegin(GL_QUADS)
for i in range(4):
glTexCoord3f(texco[i][0], texco[i][1], 0.0)
glVertex2f(verco[eye][i][0], verco[eye][i][1])
glEnd()
def loop(self, scale):
"""
Draw in the preview window
"""
if not scale:
return
act_tex = Buffer(GL_INT, 1)
glGetIntegerv(GL_TEXTURE_2D, act_tex)
if scale != 100:
viewport = Buffer(GL_INT, 4)
glGetIntegerv(GL_VIEWPORT, viewport)
width = int(scale * 0.01 * viewport[2])
height = int(scale * 0.01 * viewport[3])
glViewport(viewport[0], viewport[1], width, height)
glScissor(viewport[0], viewport[1], width, height)
glDisable(GL_DEPTH_TEST)
view_setup()
glEnable(GL_TEXTURE_2D)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self._color_texture_left)
self._drawRectangle(0)
glBindTexture(GL_TEXTURE_2D, self._color_texture_right)
self._drawRectangle(1)
glBindTexture(GL_TEXTURE_2D, act_tex[0])
glDisable(GL_TEXTURE_2D)
view_reset()
if scale != 100:
glViewport(viewport[0], viewport[1], viewport[2], viewport[3])
glScissor(viewport[0], viewport[1], viewport[2], viewport[3])
virtual_reality_viewport-2df81ca6ea2fd4117efa1aafc63b97a35bae2d92/space_view3d_virtual_reality/ui.py0000664 0000000 0000000 00000004630 12724054714 0033167 0 ustar 00root root 0000000 0000000 import bpy
# ############################################################
# User Interface
# ############################################################
class VirtualRealityPanel(bpy.types.Panel):
bl_label = "Head Mounted Display"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_category = 'Virtual Reality'
@staticmethod
def draw(self, context):
layout = self.layout
wm = context.window_manager
vr = wm.virtual_reality
col = layout.column()
if not vr.is_enabled:
col.operator("view3d.virtual_reality_display", text="Virtual Reality").action='ENABLE'
else:
col.operator("view3d.virtual_reality_display", text="Virtual Reality", icon="X").action='DISABLE'
box = col.box()
col = box.column()
if vr.is_slave_setup:
col.operator("view3d.virtual_reality_display", text="Start", icon="CAMERA_STEREO").action='FULLSCREEN'
else:
if vr.is_paused:
col.operator("view3d.virtual_reality_display", text="Play", icon="PLAY").action='PLAY'
else:
col.operator("view3d.virtual_reality_display", text="Pause", icon="PAUSE").action='PAUSE'
row = col.row()
row.prop(vr, "use_preview")
sub = row.column()
sub.active = vr.use_preview
sub.prop(vr, "preview_scale", text="Scale")
sub = col.column()
sub.active = not (vr.use_preview and vr.preview_scale == 100)
sub.prop(vr, "use_hmd_only")
col.operator("view3d.virtual_reality_display", text="Re-Center").action='RECENTER'
col.label(text="Tracking:")
col.row().prop(vr, "tracking_mode", expand=True)
col.prop(vr, "lock_camera")
if vr.error_message:
col.separator()
col.label(text=vr.error_message)
#col.separator()
#col.prop(vr, "is_debug")
# ############################################################
# Un/Registration
# ############################################################
def register():
bpy.utils.register_class(VirtualRealityPanel)
def unregister():
bpy.utils.unregister_class(VirtualRealityPanel)