pax_global_header 0000666 0000000 0000000 00000000064 12607774204 0014523 g ustar 00root root 0000000 0000000 52 comment=611030b44bf2306f7766c0d94d185033aed047c6
virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/ 0000775 0000000 0000000 00000000000 12607774204 0023364 5 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/.gitignore 0000664 0000000 0000000 00000000006 12607774204 0025350 0 ustar 00root root 0000000 0000000 *.swp
virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/.gitmodules 0000664 0000000 0000000 00000001013 12607774204 0025534 0 ustar 00root root 0000000 0000000 [submodule "space_view3d_virtual_reality/lib/python-ovrsdk"]
path = space_view3d_virtual_reality/lib/python-ovrsdk
url = https://github.com/dfelinto/python-ovrsdk.git
[submodule "space_view3d_virtual_reality/oculus_sdk_bridge"]
path = space_view3d_virtual_reality/oculus_sdk_bridge
url = http://git.impa.br/dfelinto/oculus_sdk_bridge.git
[submodule "space_view3d_virtual_reality/lib/oculus_sdk_bridge"]
path = space_view3d_virtual_reality/lib/oculus_sdk_bridge
url = http://git.impa.br/dfelinto/oculus_sdk_bridge.git
virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/README.md 0000664 0000000 0000000 00000004624 12607774204 0024651 0 ustar 00root root 0000000 0000000 # Virtual Reality Viewport
Addon to bring virtual reality devices to the Blender viewport.
This is work in progress/pre-alpha state, use at your own risk.
Prerequisite
============
Currently working on Blender Test Build 2.75: http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.75
How to Use
==========
In the viewport press ``Space`` + ``Virtual Reality Viewport``.
And then press:
* ``Alt + F11`` (Window Fullscreen)
* ``Alt + F10`` (Fullscreen Area and Hide Panels)
Current State
=============
Video of plugin working:
[![Video of plugin in action](http://img.youtube.com/vi/saSn2qvW0aE/0.jpg)](https://www.youtube.com/watch?v=saSn2qvW0aE)
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
$ 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.75/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.75/scripts/addons/
```
Optionally, instead of rsync you can generate a new ``.zip``, remove the previous version of the addon and re-install it.
Roadmap
=======
Oculus DK2 is currently working. The next step is to support view navigation (`Shift + F`) within the addon.
Later we can make it flexible enough to support other HMD devices.
There are a few things I still need to implement:
* Automatically go to clean fullscreen (Alt+F10) and fullwindow (Alt+F11)
Feel free to send pull requests to any of the above.
Credits
=======
Oculus DK2 Shader by Martins Upitis (which I guess based his work from elsewhere)
OculusVR wrapper by https://github.com/jherico/python-ovrsdk
Blender Addon - Dalai Felinto - http://www.dalaifelinto.com
virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality/ 0000775 0000000 0000000 00000000000 12607774204 0031237 5 ustar 00root root 0000000 0000000 __init__.py 0000664 0000000 0000000 00000004573 12607774204 0033302 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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",
)
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-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality/hmd/ 0000775 0000000 0000000 00000000000 12607774204 0032007 5 ustar 00root root 0000000 0000000 __init__.py 0000664 0000000 0000000 00000020327 12607774204 0034045 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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_object",
"_framebuffer_object",
"_color_object",
"_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._framebuffer_object = [0, 0]
self._color_object = [0, 0]
self._offscreen_object = [None, None]
self._eye_orientation_raw = [[i for i in range(4)], [i for i in range(4)]]
self._eye_position_raw = [[i for i in range(3)], [i for i in range(3)]]
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_object(self):
return self._offscreen_object[self._current_eye]
@property
def framebuffer_object(self):
return self._framebuffer_object[self._current_eye]
@property
def color_object(self):
return self._color_object[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_object[i] = gpu.offscreen.new(self._width[i], self._height[i], 0)
self._framebuffer_object[i] = self._offscreen_object[i].framebuffer_object
self._color_object[i] = self._offscreen_object[i].color_object
except Exception as E:
print(E)
self._offscreen_object[0] = None
self._offscreen_object[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_object[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
"""
tracking_mode = context.window_manager.virtual_reality.tracking_mode
view_matrix = self._getViewMatrix(context)
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):
region = context.region_data
if region.view_perspective == 'CAMERA':
space = context.space_data
camera = space.camera
return camera.matrix_world.copy().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 00000002700 12607774204 0033367 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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()')
super(Debug, self).loop(context)
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()
def updateMatrices(self, context):
"""
Update OpenGL drawing matrices
"""
camera = context.scene.camera
modelview_matrix = camera.matrix_world.inverted()
projection_matrix = camera.calc_matrix_camera()
for i in range(2):
self._modelview_matrix[i] = modelview_matrix
self._projection_matrix[i] = projection_matrix
oculus.py 0000664 0000000 0000000 00000006175 12607774204 0033625 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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/oculus_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('oculus_sdk_bridge')
def _getHMDClass(self):
from bridge.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
super(Oculus, self).init()
# send it back to HMD
if not self._setup():
raise Exception("Failed to setup HMD")
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._framebuffer_object[0], self._framebuffer_object[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 00000011547 12607774204 0035150 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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('python-ovrsdk')
def _getHMDClass(self):
return HMD
def _setup(self):
return self._hmd.setup(self._color_object[0], self._color_object[1])
checkModule('oculus_sdk_bridge')
from bridge.hmd import HMD as wrapperHMD
class HMD(wrapperHMD):
def __init__(self):
super(HMD, self).__init__()
import oculusvr as ovr
from oculusvr import Hmd, ovrGLTexture, ovrPosef, ovrVector3f
from time import sleep
try:
Hmd.initialize()
sleep(0.5)
except SystemError as err:
self.error("__init__", Exception("Oculus initialization failed, check the physical connections and run again", True))
return
try:
debug = not Hmd.detect()
self._device = Hmd(debug=debug)
desc = self._device.hmd.contents
self._frame = -1
sleep(0.1)
self._device.configure_tracking()
self._fovPorts = (
desc.DefaultEyeFov[0],
desc.DefaultEyeFov[1],
)
self._eyeTextures = [ ovrGLTexture(), ovrGLTexture() ]
self._eyeOffsets = [ ovrVector3f(), ovrVector3f() ]
rc = ovr.ovrRenderAPIConfig()
header = rc.Header
header.API = ovr.ovrRenderAPI_OpenGL
header.BackBufferSize = desc.Resolution
header.Multisample = 1
for i in range(8):
rc.PlatformData[i] = 0
self._eyeRenderDescs = self._device.configure_rendering(rc, self._fovPorts)
for eye in range(2):
size = self._device.get_fov_texture_size(eye, self._fovPorts[eye])
self._width[eye], self._height[eye] = size.w, size.h
eyeTexture = self._eyeTextures[eye]
eyeTexture.API = ovr.ovrRenderAPI_OpenGL
header = eyeTexture.Texture.Header
header.TextureSize = size
vp = header.RenderViewport
vp.Size = size
vp.Pos.x = 0
vp.Pos.y = 0
self._eyeOffsets[eye] = self._eyeRenderDescs[eye].HmdToEyeViewOffset
# Bug in the SDK leaves a program bound, so clear it
import bgl
bgl.glUseProgram(0)
except Exception as E:
raise E
def __del__(self):
import oculusvr as ovr
if self._device:
self._device.destroy()
self._device = None
ovr.Hmd.shutdown()
def _updateProjectionMatrix(self, near, far):
import oculusvr as ovr
self.projection_matrix_left = ovr.Hmd.get_perspective(self._fovPorts[0], near, far, True).toList()
self.projection_matrix_right = ovr.Hmd.get_perspective(self._fovPorts[1], near, far, True).toList()
def setup(self, color_left, color_right):
"""
Initialize device
:param color_object_left: color object created externally
:type color_object_left: GLuint
:param color_object_right: color object created externally
:type color_object_right: GLuint
:return: return True if the device was properly initialized
:rtype: bool
"""
import oculusvr as ovr
# disable safety warning
ovr.ovrHmd_DismissHSWDisplay(self._device.hmd)
self._eyeTextures[0].OGL.TexId = color_left
self._eyeTextures[1].OGL.TexId = color_right
return True
def update(self):
"""
Get fresh tracking data
:return: return left orientation, left_position, right_orientation, right_position
:rtype: tuple(list(4), list(3), list(4), list(3))
"""
self._frame += 1
poses = self._device.get_eye_poses(self._frame, self._eyeOffsets)
self._device.begin_frame(self._frame)
for eye in range(2):
self._orientation[eye] = poses[eye].Orientation.toList()
self._position[eye] = poses[eye].Position.toList()
self._poses = poses
return super(HMD, self).update()
def frameReady(self):
"""
The frame is ready to be send to the device
:return: return True if success
:rtype: bool
"""
self._device.end_frame(self._poses, self._eyeTextures)
return True
def reCenter(self):
"""
Re-center the HMD device
:return: return True if success
:rtype: bool
"""
self._device.recenter_pose()
return True
lib.py 0000664 0000000 0000000 00000001152 12607774204 0032277 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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)
virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality/lib/ 0000775 0000000 0000000 00000000000 12607774204 0032005 5 ustar 00root root 0000000 0000000 oculus_sdk_bridge/ 0000775 0000000 0000000 00000000000 12607774204 0035415 5 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality/lib python-ovrsdk/ 0000775 0000000 0000000 00000000000 12607774204 0034555 5 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality/lib opengl_helper.py 0000664 0000000 0000000 00000027164 12607774204 0034367 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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 00000035670 12607774204 0033400 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality import bpy
from bpy.app.handlers import persistent
from .hmd import HMD
from .preview import Preview
from .lib import (
getDisplayBackend,
)
TODO = False
# ############################################################
# Commands
# ############################################################
class Commands:
recenter = 'RECENTER'
fullscreen = 'FULLSCREEN'
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
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 = ""
_hmd = None
_timer = None
_handle = None
_hash_slave = -1
_hash_master = -1
_slave_status = 0
_slave_window = None
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"),
),
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':
TODO # only if extended mode
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'}
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
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:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
self._handle = None
self._preview.quit()
if self._hmd:
self._hmd.quit()
if self._slave_window:
if hasattr(self._slave_window, "close"):
self._slave_window.close()
else:
print("Error closing HMD window")
# cleanup viewport
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 = ""
vr.is_slave_setup = False
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 = 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:
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_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])
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:
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
self.quit(context)
self._slave_window = context.window
return ok
def _slaveHook(self, context, mode=''):
self._hash_slave = -1
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
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
"""
self._hmd.loop(context)
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
# drawing
offscreen_object.draw_view3d(projection_matrix, modelview_matrix)
self._hmd.frameReady()
def _drawMaster(self, context):
wm = context.window_manager
vr = wm.virtual_reality
if self._hmd.is_direct_mode:
self._commands(context)
self._loop(context)
if vr.use_preview:
self._preview.loop(vr.preview_scale)
def _drawSlave(self, context):
if self._hmd.is_direct_mode:
return
self._commands(context)
if self._slave_status == SlaveStatus.ready:
self._loop(context)
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
"""
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)
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)
position(font_id, x, y, 0)
size(font_id, font_size, 72)
draw(font_id, "1. Move this window to the external HMD display")
position(font_id, x, y - int(font_size * 1.5), 0)
draw(font_id, "2. Select \"Start\" in the main window")
disable(font_id, SHADOW)
def _draw_callback_px(self, context):
"""
callback function, run every time the viewport is refreshed
"""
area = context.area
hash_area = hash(area)
if (hash_area == self._hash_slave):
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.fullscreen, "Fullscreen", ""),
(Commands.test, "Test", ""),
),
default="NONE",
)
class VirtualRealityInfo(bpy.types.PropertyGroup):
is_enabled = BoolProperty(
name="Enabled",
default=False,
)
use_preview = BoolProperty(
name="Preview",
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",
)
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
def reset(self):
while self.commands:
self.commands.remove(0)
self.use_preview = False
self.error_message = ""
self.is_enabled = False
self.is_slave_setup = 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 00000006035 12607774204 0033217 0 ustar 00root root 0000000 0000000 virtual_reality_viewport-611030b44bf2306f7766c0d94d185033aed047c6/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_object_left",
"_color_object_right",
}
def init(self, color_object_left, color_object_right):
"""
Initialize preview window
:param color_object_left: 2D Texture binding ID (bind to the Framebuffer Object) for left eye
:type color_object_left: bgl.GLuint
:param color_object_right: 2D Texture binding ID (bind to the Framebuffer Object) for right eye
:type color_object_right: bgl.GLuint
"""
self.update(color_object_left, color_object_right)
def quit(self):
"""
Destroy preview window
"""
pass
def update(self, color_object_left, color_object_right):
"""
Update OpenGL binding textures
:param color_object_left: 2D Texture binding ID (bind to the Framebuffer Object) for left eye
:type color_object_left: bgl.GLuint
:param color_object_right: 2D Texture binding ID (bind to the Framebuffer Object) for right eye
:type color_object_right: bgl.GLuint
"""
self._color_object_left = color_object_left
self._color_object_right = color_object_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_object_left)
self._drawRectangle(0)
glBindTexture(GL_TEXTURE_2D, self._color_object_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-611030b44bf2306f7766c0d94d185033aed047c6/space_view3d_virtual_reality/ui.py0000664 0000000 0000000 00000003454 12607774204 0032234 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", icon="PLAY").action='ENABLE'
else:
col.operator("view3d.virtual_reality_display", text="Virtual Reality", icon="X").action='DISABLE'
col.separator()
if vr.is_slave_setup:
col.operator("view3d.virtual_reality_display", text="Start", icon="CAMERA_STEREO").action='FULLSCREEN'
else:
row = col.row()
row.prop(vr, "use_preview")
sub = row.column()
sub.active = vr.use_preview
sub.prop(vr, "preview_scale", text="Scale")
col.separator()
col.operator("view3d.virtual_reality_display", text="Re-Center").action='RECENTER'
col.separator()
col.label(text="Tracking:")
col.row().prop(vr, "tracking_mode", expand=True)
col.separator()
col.label(text=vr.error_message)
# ############################################################
# Un/Registration
# ############################################################
def register():
bpy.utils.register_class(VirtualRealityPanel)
def unregister():
bpy.utils.unregister_class(VirtualRealityPanel)