diff options
Diffstat (limited to 'src/mari')
-rw-r--r-- | src/mari/1.4v1/README | 10 | ||||
-rwxr-xr-x | src/mari/1.4v1/_ocio_filter.py | 127 | ||||
-rwxr-xr-x | src/mari/1.4v1/_ocio_toolbar.py | 1279 | ||||
-rwxr-xr-x | src/mari/1.4v1/ocio.py | 789 | ||||
-rw-r--r-- | src/mari/prototype/README | 3 | ||||
-rw-r--r-- | src/mari/prototype/ociodisplay.py | 242 | ||||
-rw-r--r-- | src/mari/prototype/ociofiletransform.py | 93 |
7 files changed, 2543 insertions, 0 deletions
diff --git a/src/mari/1.4v1/README b/src/mari/1.4v1/README new file mode 100644 index 0000000..ecfdf18 --- /dev/null +++ b/src/mari/1.4v1/README @@ -0,0 +1,10 @@ +These files ship with Mari, and are *not* required to be manually installed. + +They are provided as a reference example of using the OCIO API to create a GPU +monitor implementation in python. + +Media/Scripts/mari/utils/ocio.py +Media/Scripts/mari/system/_ocio_toolbar.py +Media/Scripts/mari/system/_ocio_filter.py + +All code in these examples is Copyright (c) 2011 The Foundry Visionmongers Ltd. diff --git a/src/mari/1.4v1/_ocio_filter.py b/src/mari/1.4v1/_ocio_filter.py new file mode 100755 index 0000000..e3c3bf3 --- /dev/null +++ b/src/mari/1.4v1/_ocio_filter.py @@ -0,0 +1,127 @@ +#------------------------------------------------------------------------------- +# Post processing (color management) related Mari scripts +# coding: utf-8 +# Copyright (c) 2011 The Foundry Visionmongers Ltd. All Rights Reserved. +#------------------------------------------------------------------------------- + +import mari, time, PythonQt, os, math +ocio = mari.utils.ocio + +############################################################################################## + +filter = None + +class OcioFilter(): + + #----------------------------------------------------------------------------------------- + + def __init__(self): + # Default all members... + self._config_file_list = mari.FileList(ocio.config_file_list_default) + self._config = ocio.config_default + self._input_color_space = ocio.color_space_default + self._output_color_space = ocio.color_space_default + self._filter = mari.gl_render.createQuickApplyGLSL('Color Correction', '', '', 'ColorManager.png') + self._filter_cache_id = None + self._texture_cache_id = None + self._sampler_name = None + + self._filter.setMetadata('ConfigPath', self._config_file_list) + self._filter.setMetadataDisplayName('ConfigPath', 'Configuration File') + self._filter.setMetadataDefault('ConfigPath', ocio.CONFIG_FILE_LIST_RESET) + self._filter.setMetadataFlags('ConfigPath', self._filter.METADATA_VISIBLE | self._filter.METADATA_EDITABLE) + + color_spaces = [color_space.getName() for color_space in self._config.getColorSpaces()] + + color_space_reset = ocio.COLOR_SPACE_RESET + if color_spaces.count(color_space_reset) == 0: + color_space_reset = color_spaces[0] + + self._filter.setMetadata('InputColorSpace', self._input_color_space) + self._filter.setMetadataDisplayName('InputColorSpace', 'Input Color Space') + self._filter.setMetadataDefault('InputColorSpace', color_space_reset) + self._filter.setMetadataItemList('InputColorSpace', color_spaces) + self._filter.setMetadataFlags('InputColorSpace', self._filter.METADATA_VISIBLE | self._filter.METADATA_EDITABLE) + + self._filter.setMetadata('OutputColorSpace', self._output_color_space) + self._filter.setMetadataDisplayName('OutputColorSpace', 'Output Color Space') + self._filter.setMetadataDefault('OutputColorSpace', color_space_reset) + self._filter.setMetadataItemList('OutputColorSpace', color_spaces) + self._filter.setMetadataFlags('OutputColorSpace', self._filter.METADATA_VISIBLE | self._filter.METADATA_EDITABLE) + + mari.utils.connect(self._filter.metadataValueChanged, self._metadataValueChanged) + + self._rebuildFilter() + + #----------------------------------------------------------------------------------------- + + def _metadataValueChanged(self, name, value): + ocio.printMessage(ocio.MessageType.DEBUG, 'Metadata \'%s\' changed to \'%s\'' % (name, value)) + + if name == 'ConfigPath': + if value == self._config_file_list: + return + self._config_file_list = mari.FileList(value) + self._config = ocio.loadConfig(self._config_file_list.at(0), False) + + color_spaces = [color_space.getName() for color_space in self._config.getColorSpaces()] + + color_space_reset = ocio.COLOR_SPACE_RESET + if color_spaces.count(color_space_reset) == 0: + color_space_reset = color_spaces[0] + + if color_spaces.count(self._input_color_space) == 0: + self._input_color_space = color_space_reset + + if color_spaces.count(self._output_color_space) == 0: + self._output_color_space = color_space_reset + + self._filter.setMetadataItemList('InputColorSpace', color_spaces) + self._filter.setMetadataItemList('OutputColorSpace', color_spaces) + self._filter.setMetadataDefault('InputColorSpace', color_space_reset) + self._filter.setMetadataDefault('OutputColorSpace', color_space_reset) + self._filter.setMetadata('InputColorSpace', self._input_color_space ) + self._filter.setMetadata('OutputColorSpace', self._output_color_space) + + elif name == 'InputColorSpace': + if value == self._input_color_space: + return + self._input_color_space = value + + elif name == 'OutputColorSpace': + if value == self._output_color_space: + return + self._output_color_space = value + + else: + return + + self._rebuildFilter() + + #----------------------------------------------------------------------------------------- + + def _rebuildFilter(self): + input_color_space = self._config.getColorSpace(self._input_color_space) + if input_color_space is not None: + output_color_space = self._config.getColorSpace(self._output_color_space) + if output_color_space is not None: + processor = self._config.getProcessor(input_color_space, output_color_space) + + self._filter_cache_id, self._texture_cache_id, self._sampler_name = ocio.buildProcessorFilter( + processor, + self._filter, + self._filter_cache_id, + self._texture_cache_id) + + current_canvas = mari.canvases.current() + if current_canvas is not None: + current_canvas.repaint() + +############################################################################################## + +if mari.app.isRunning(): + if not hasattr(mari.gl_render, 'createQuickApplyGLSL'): + printMessage(MessageType.ERROR, 'This version of Mari does not support the mari.gl_render.createQuickApplyGLSL API') + + elif ocio is not None: + filter = OcioFilter() diff --git a/src/mari/1.4v1/_ocio_toolbar.py b/src/mari/1.4v1/_ocio_toolbar.py new file mode 100755 index 0000000..84afb68 --- /dev/null +++ b/src/mari/1.4v1/_ocio_toolbar.py @@ -0,0 +1,1279 @@ +#------------------------------------------------------------------------------- +# Post processing (color management) related Mari scripts +# coding: utf-8 +# Copyright (c) 2011 The Foundry Visionmongers Ltd. All Rights Reserved. +#------------------------------------------------------------------------------- + +import mari, time, PythonQt, os, math +QtGui = PythonQt.QtGui +QtCore = PythonQt.QtCore +ocio = mari.utils.ocio + +############################################################################################## + +GAIN_GROUP_MAX_WIDTH = 312 +FSTOP_MAX_WIDTH = 50 +EXPOSURE_MAX_WIDTH = 102 +GAIN_MAX_WIDTH = 80 +GAMMA_MAX_WIDTH = 200 +TOOLBAR_SPACING = 3 + +toolbar = None + +class OcioToolBar(): + + #----------------------------------------------------------------------------------------- + + def __init__(self): + # Default all members... + self._config_file_list = mari.FileList(ocio.config_file_list_default) + self._config = ocio.config_default + self._lut_file_list = mari.FileList(ocio.lut_file_list_default) + self._lut_extrapolate = ocio.lut_extrapolate_default + self._color_space = ocio.color_space_default + self._display = ocio.display_default + self._view = ocio.view_default + self._swizzle = ocio.swizzle_default + self._gain = ocio.gain_default + self._gamma = ocio.gamma_default + + self._lut_filter = None + self._lut_filter_cache_id = None + self._lut_texture_cache_id = None + self._lut_sampler_name = None + + self._display_filter = None + self._display_filter_cache_id = None + self._display_texture_cache_id = None + self._display_sampler_name = None + + self._lut_extrapolate_widget = None + self._color_space_widget = None + self._display_widget = None + self._view_widget = None + self._swizzle_widget = None + self._fstop_widget = None + self._fstop_decrement_widget = None + self._fstop_increment_widget = None + self._gain_widget = None + self._exposure_widget = None + self._gain_reset_widget = None + self._gamma_widget = None + self._gamma_reset_widget = None + + self._buildWidgets() + self._toggle_color_management_action.setEnabled(False) + self._enableWidgets(False) + + # Enable/disable color management. + mari.gl_render.setPostProcessingEnabled(self.isColorManagementEnabled()) + + # *** IMPORTANT *** The post filter collection used to be called 'OpenColorIO' but was renamed to hide the fact + # we use OpenColorIO from our users. So as a temporary workaround we need to check for the old filter collection + # on startup and remove it if found. + delete_filter_collection = mari.gl_render.findPostFilterCollection('OpenColorIO') + if delete_filter_collection is not None: + mari.gl_render.deletePostFilterCollection(delete_filter_collection) + + # Create the OCIO post filter collection if not present. + self._filter_collection = mari.gl_render.findPostFilterCollection('Color Space') + if self._filter_collection is None: + self._filter_collection = mari.gl_render.createPostFilterCollection('Color Space') + else: + self._filter_collection.clear() + self._filter_collection.setReadOnly(True) + + self._lut_filter = self._filter_collection.createGLSL('LUT Transform') + if not self._lut_file_list.isEmpty() and not self._rebuildLUTFilter(self._lut_file_list.at(0)): + self._lut_file_list.clear() + + self._display_filter = self._filter_collection.createGLSL('Display Transform') + self._rebuildDisplayFilter() + + self._buildMetadata() + + # Set the color management filter stack as the current. + mari.gl_render.setPostFilterCollection(self._filter_collection) + + # Attach ourselves to the applications toolbar created signal so we can rebuild the toolbar when it's been + # destoyed. + mari.utils.connect(mari.app.toolBarsCreated, self._toolBarsCreated) + + # Attach ourselves to the appropriate GL signals so we can enable and disable widgets. + mari.utils.connect(mari.gl_render.postProcessingEnabled, self._postProcessingEnabled) + mari.utils.connect(mari.gl_render.setCurrentPostFilterCollection, self._setCurrentPostFilterCollection) + + # Attach ourselves to the appropriate project signals so we can load and save settings. + mari.utils.connect(mari.projects.openedProject, self._openedProject) + mari.utils.connect(mari.projects.aboutToSaveProject, self._aboutToSaveProject) + mari.utils.connect(mari.projects.projectClosed, self._closedProject) + + # Update the UI to match the current project, if we have one. + current_project = mari.projects.current() + if current_project is not None: + self._openedProject(current_project) + + #----------------------------------------------------------------------------------------- + + def isColorManagementEnabled(self): + return self._toggle_color_management_action.isChecked() + + #----------------------------------------------------------------------------------------- + + def setLUTPath(self, value, update_metadata = True, force_shader_build = False): + if (self._lut_file_list.isEmpty() and value != '') or \ + (not self._lut_file_list.isEmpty() and value == '') or \ + (not self._lut_file_list.isEmpty() and value != self._lut_file_list.at(0)) \ + : + if self._rebuildLUTFilter(value, force_shader_build): + self._lut_file_list.clear() + if value != '': + self._lut_file_list.append(value) + self._lut_file_list.setPickedFile(value) + + self._clear_lut_action.setEnabled(True) + self._lut_extrapolate_widget.setEnabled(True) + self._lut_filter.setEnabled(True) + else: + self._clear_lut_action.setEnabled(False) + self._lut_extrapolate_widget.setEnabled(False) + self._lut_filter.setEnabled(False) + + if update_metadata: + mari.utils.disconnect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + self._lut_filter.setMetadata('File', self._lut_file_list) + mari.utils.connect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + + else: + # If this was a request via the metadata system we will need to put the value back to what it was + # before. + if not update_metadata: + mari.utils.disconnect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + self._lut_filter.setMetadata('File', self._lut_file_list) + mari.utils.connect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + + return False + + return True + + #----------------------------------------------------------------------------------------- + + def resetLUT(self): + if ocio.lut_file_list_default.isEmpty() or not self.setLUTPath(ocio.lut_file_list_default.at(0)): + self.setLUTPath('') + + #----------------------------------------------------------------------------------------- + + def selectLUT(self): + lut_path = mari.utils.misc.getOpenFileName(None, + 'Select LUT File', + '' if self._lut_file_list.isEmpty() else self._lut_file_list.at(0), + ocio.lutFileFilter(), + None, + 0) + if os.path.isfile(lut_path): + self.setLUTPath(lut_path) + + #----------------------------------------------------------------------------------------- + + def setExtrapolateEnabled(self, value, update_widget = True, update_metadata = True): + if value != self._lut_extrapolate: + self._lut_extrapolate = value + + if update_widget: + block = self._lut_extrapolate_widget.blockSignals(True) + self._lut_extrapolate_widget.setChecked(self._lut_extrapolate) + self._lut_extrapolate_widget.blockSignals(block) + + if update_metadata: + mari.utils.disconnect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + self._lut_filter.setMetadata('Extrapolate', self._lut_extrapolate) + mari.utils.connect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + + if not self._rebuildLUTFilter(lut_path = '' if self._lut_file_list.isEmpty() else self._lut_file_list.at(0), + force_shader_build = True): + self.resetLUT() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed extrapolate to \'%s\'' % self._lut_extrapolate) + + #----------------------------------------------------------------------------------------- + + def setConfigPath(self, value, update_metadata = True): + if self._config_file_list.isEmpty() or value != self._config_file_list.at(0): + config = ocio.loadConfig(value, True) + if config is not None: + self._config_file_list.clear() + self._config_file_list.append(value) + self._config_file_list.setPickedFile(value) + + self._config = config + + self._updateDisplayWidgets() + self._updateDisplayMetadata() + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed config to \'%s\'' % self._config_file_list.at(0)) + + else: + # If this was a request via the metadata system we will need to put the value back to what it was + # before. + if not update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('ConfigPath', self._config_file_list) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + return False + + return True + + #----------------------------------------------------------------------------------------- + + def selectConfig(self): + config_path = mari.utils.misc.getOpenFileName(None, + 'Select Configuration File', + '' if self._config_file_list.isEmpty() else self._config_file_list.at(0), + ocio.configFileFilter(), + None, + 0) + if os.path.isfile(config_path): + self.setConfigPath(config_path) + + #----------------------------------------------------------------------------------------- + + def setColorSpace(self, value, update_widget = True, update_metadata = True): + if value != self._color_space: + self._color_space = value + + if update_widget: + block = self._color_space_widget.blockSignals(True) + index = self._color_space_widget.findText(self._color_space) + self._color_space_widget.setCurrentIndex(index) + self._color_space_widget.blockSignals(block) + + if update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('InputColorSpace', self._color_space) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed input color space to \'%s\'' % self._color_space) + + #----------------------------------------------------------------------------------------- + + def setDisplay(self, value, update_widget = True, update_metadata = True): + if value != self._display: + self._display = value + + if update_widget: + block = self._display_widget.blockSignals(True) + index = self._display_widget.findText(self._display) + self._display_widget.setCurrentIndex(index) + self._display_widget.blockSignals(block) + + if update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('Display', self._display) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self.setView(self._config.getDefaultView(self._display), update_widget, update_metadata) + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed display to \'%s\'' % self._display) + + #----------------------------------------------------------------------------------------- + + def setView(self, value, update_widget = True, update_metadata = True): + if value != self._view: + self._view = value + + if update_widget: + block = self._view_widget.blockSignals(True) + index = self._view_widget.findText(self._view) + self._view_widget.setCurrentIndex(index) + self._view_widget.blockSignals(block) + + if update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('View', self._view) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed view to \'%s\'' % self._view) + + #----------------------------------------------------------------------------------------- + + def setSwizzle(self, value, update_widget = True, update_metadata = True): + if value != self._swizzle: + self._swizzle = value + + if update_widget: + block = self._swizzle_widget.blockSignals(True) + index = self._swizzle_widget.findText(self._swizzle) + self._swizzle_widget.setCurrentIndex(index) + self._swizzle_widget.blockSignals(block) + + if update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('Swizzle', self._swizzle) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed swizzle to \'%s\'' % self._swizzle) + + #----------------------------------------------------------------------------------------- + + def setGain(self, value, update_widget = True, update_metadata = True): + if value != self._gain: + self._gain = value + + if update_widget: + self._updateGainWidgets() + + if update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('Gain', self._gain) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed gain to \'%s\'' % self._gain) + + #----------------------------------------------------------------------------------------- + + def setGamma(self, value, update_widget = True, update_metadata = True): + if value != self._gamma: + self._gamma = value + + if update_widget: + block = self._gamma_widget.blockSignals(True) + self._gamma_widget.setValue(self._gamma) + self._gamma_widget.blockSignals(block) + + if update_metadata: + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._display_filter.setMetadata('Gamma', self._gamma) + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self._rebuildDisplayFilter() + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed gamma to \'%s\'' % self._gamma) + + #----------------------------------------------------------------------------------------- + + def updateLUTSize(self): + ocio.printMessage(ocio.MessageType.DEBUG, 'Updating LUT size...') + + # Rebuild the LUT filter. + if self._lut_sampler_name is not None: + self._lut_filter.deleteTexture(self._lut_sampler_name) + self._lut_sampler_name = None + + self._lut_filter_cache_id = None + self._lut_texture_cache_id = None + + if not self._rebuildLUTFilter(lut_path = '' if self._lut_file_list.isEmpty() else self._lut_file_list.at(0), + force_shader_build = True): + self.resetLUT() + + # Rebuild the display filter. + if self._display_sampler_name is not None: + self._display_filter.deleteTexture(self._display_sampler_name) + self._display_sampler_name = None + + self._display_filter_cache_id = None + self._display_texture_cache_id = None + + self._rebuildDisplayFilter() + + #----------------------------------------------------------------------------------------- + + def updateFStopCenter(self): + ocio.printMessage(ocio.MessageType.DEBUG, 'Updating f-stop center...') + + fstop = ocio.convertGainToFStop(self._gain) + self._updateFStopWidgetText(fstop) + + #----------------------------------------------------------------------------------------- + # Widgets: + #----------------------------------------------------------------------------------------- + + def _buildWidgets(self): + action_list = list() + + self._toggle_color_management_action = self._addAction( + '/Mari/OpenColorIO/&Toggle Color Management', + 'mari.system._ocio_toolbar.toolbar._toggleColorManagement()', + 'ColorManager.png', + 'Toggle on/off color management', + 'Toggle color management') + self._toggle_color_management_action.setCheckable(True) + self._toggle_color_management_action.setChecked(ocio.enabled_default) + action_list.append('/Mari/OpenColorIO/&Toggle Color Management') + + self._select_config_action = self._addAction( + '/Mari/OpenColorIO/&Select Config', + 'mari.system._ocio_toolbar.toolbar.selectConfig()', + 'LoadColorConfig.png', + 'Select color space configuration file', + 'Select config') + action_list.append('/Mari/OpenColorIO/&Select Config') + + self._select_lut_action = self._addAction( + '/Mari/OpenColorIO/&Select LUT', + 'mari.system._ocio_toolbar.toolbar.selectLUT()', + 'LoadLookupTable.png', + 'Select LUT file', + 'Select LUT') + action_list.append('/Mari/OpenColorIO/&Select LUT') + + self._clear_lut_action = self._addAction( + '/Mari/OpenColorIO/&Clear LUT', + 'mari.system._ocio_toolbar.toolbar._clearLUT()', + 'ClearLookupTable.png', + 'Clear current LUT', + 'Clear LUT') + action_list.append('/Mari/OpenColorIO/&Clear LUT') + + mari.app.deleteToolBar('Color Space') + self._toolbar = mari.app.createToolBar('Color Space', True) + self._toolbar.addActionList(action_list, False) + self._toolbar.setLockedSlot(True) + self._toolbar.setSpacing(TOOLBAR_SPACING) + + self._toolbar.insertSeparator('/Mari/OpenColorIO/&Select LUT') + + # Extrapolate: + self._toolbar.addWidget(QtGui.QLabel('Extrapolate')) + self._lut_extrapolate_widget = QtGui.QCheckBox() + self._lut_extrapolate_widget.setToolTip('Extrapolate if outside LUT range'); + self._lut_extrapolate_widget.setChecked(self._lut_extrapolate) + self._lut_extrapolate_widget.connect( + QtCore.SIGNAL('toggled(bool)'), + lambda value: self.setExtrapolateEnabled(value = value, update_widget = False, update_metadata = True)) + self._toolbar.addWidget(self._lut_extrapolate_widget) + + self._toolbar.addSeparator() + + color_spaces = [color_space.getName() for color_space in self._config.getColorSpaces()] + + # Color-Space: + self._color_space_widget = self._addComboBox( + 'Input Color Space', + color_spaces, + self._color_space, + ocio.color_space_default, + lambda value: self.setColorSpace(value = value, update_widget = False, update_metadata = True)) + self._color_space = self._color_space_widget.currentText + + # Display: + self._display_widget = self._addComboBox( + 'Display Device', + self._config.getDisplays(), + self._display, + ocio.display_default, + lambda value: self.setDisplay(value = value, update_widget = False, update_metadata = True)) + self._display = self._display_widget.currentText + + # View: + self._view_widget = self._addComboBox( + 'View Transform', + self._config.getViews(self._display), + self._view, + ocio.view_default, + lambda value: self.setView(value = value, update_widget = False, update_metadata = True)) + self._view = self._view_widget.currentText + + # Swizzle: + self._swizzle_widget = self._addComboBox( + 'Component', + ocio.SWIZZLE_TYPES, + self._swizzle, + ocio.swizzle_default, + lambda value: self.setSwizzle(value = value, update_widget = False, update_metadata = True)) + self._swizzle = self._swizzle_widget.currentText + + # Gain Group: + group_widget, layout = self._addWidgetGroup() + group_widget.setMaximumWidth(GAIN_GROUP_MAX_WIDTH) + + layout.addWidget(QtGui.QLabel('Gain')) + + # F-Stop: + subgroup_widget = QtGui.QWidget() + layout.addWidget(subgroup_widget) + + sublayout = QtGui.QHBoxLayout() + sublayout.setSpacing(0) + sublayout.setMargin(0) + subgroup_widget.setLayout(sublayout) + + exposure = ocio.convertGainToExposure(self._gain) + fstop = ocio.convertExposureToFStop(exposure) + scale = (exposure - ocio.EXPOSURE_MIN) / ocio.EXPOSURE_DELTA + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.FSTOP_STEP_SIZE)) + widget_value = scale * widget_max + self._fstop_widget = mari.LineEdit() + self._fstop_widget.setRange(widget_max) + self._fstop_widget.setMaximumWidth(FSTOP_MAX_WIDTH) + self._fstop_widget.setReadOnly(True) + self._updateFStopWidgetText(fstop) + self._fstop_widget.setValue(widget_value) + mari.utils.connect(self._fstop_widget.movedMouse, self._fstopMovedMouse) + self._fstop_widget.addToLayout(sublayout) + + self._fstop_decrement_widget = self._addSmallButtom( + sublayout, + '-', + 'Decrease gain 1/2 stop', + lambda: self.setGain(ocio.convertExposureToGain(ocio.convertGainToExposure(self._gain) - 0.5))) + self._fstop_increment_widget = self._addSmallButtom( + sublayout, + '+', + 'Increase gain 1/2 stop', + lambda: self.setGain(ocio.convertExposureToGain(ocio.convertGainToExposure(self._gain) + 0.5))) + + ocio.registerLUTSizeChanged(self.updateLUTSize) + ocio.registerFStopCenterChanged(self.updateFStopCenter) + + # Gain: + subgroup_widget = QtGui.QWidget() + layout.addWidget(subgroup_widget) + + sublayout = QtGui.QHBoxLayout() + sublayout.setSpacing(3) + sublayout.setMargin(0) + subgroup_widget.setLayout(sublayout) + + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.EXPOSURE_STEP_SIZE)) + widget_value = scale * widget_max + self._gain_widget = mari.LineEdit() + self._gain_widget.setRange(widget_max) + self._gain_widget.addFloatValidator(ocio.GAIN_MIN, ocio.GAIN_MAX, ocio.GAIN_PRECISION) + self._gain_widget.setMaximumWidth(GAIN_MAX_WIDTH) + self._updateGainWidgetText() + self._gain_widget.setValue(widget_value) + mari.utils.connect( + self._gain_widget.lostFocus, + lambda: self.setGain(max(min(float(self._gain_widget.text()), ocio.GAIN_MAX), ocio.GAIN_MIN))) + mari.utils.connect(self._gain_widget.movedMouse, self._gainMovedMouse) + self._gain_widget.addToLayout(sublayout) + + # Exposure: + self._exposure_widget = QtGui.QSlider() + self._exposure_widget.orientation = 1 + self._exposure_widget.setMaximum(widget_max) + self._exposure_widget.setValue(widget_value) + self._exposure_widget.setMinimumWidth(EXPOSURE_MAX_WIDTH) + self._exposure_widget.setMaximumWidth(EXPOSURE_MAX_WIDTH) + mari.utils.connect(self._exposure_widget.valueChanged, self._exposureChanged) + sublayout.addWidget(self._exposure_widget) + + self._gain_reset_widget = self._addSmallButtom( + layout, + 'R', + 'Reset gain to default', + lambda: self.setGain(value = ocio.GAIN_RESET, update_widget = True, update_metadata = True)) + + # Gamma: + group_widget, layout = self._addWidgetGroup() + group_widget.setMaximumWidth(GAMMA_MAX_WIDTH) + + layout.addWidget(QtGui.QLabel('Gamma')) + + self._gamma_widget = mari.FloatSlider() + self._gamma_widget.setRange(ocio.GAMMA_MIN, ocio.GAMMA_MAX) + self._gamma_widget.setStepSize(ocio.GAMMA_STEP_SIZE) + self._gamma_widget.setPrecision(ocio.GAMMA_PRECISION) + self._gamma_widget.setValue(self._gamma) + mari.utils.connect( + self._gamma_widget.valueChanged, + lambda value: self.setGamma(value = value, update_widget = False, update_metadata = True)) + self._gamma_widget.addToLayout(layout) + + self._gamma_reset_widget = self._addSmallButtom( + layout, + 'R', + 'Reset gamma to default', + lambda: self.setGamma(value = ocio.GAMMA_RESET, update_widget = True, update_metadata = True)) + + #----------------------------------------------------------------------------------------- + + def _updateDisplayWidgets(self): + color_spaces = [color_space.getName() for color_space in self._config.getColorSpaces()] + + self._updateComboBox(self._color_space_widget, color_spaces, self._color_space, ocio.color_space_default) + self._color_space = self._color_space_widget.currentText + + self._updateComboBox(self._display_widget, self._config.getDisplays(), self._display, ocio.display_default) + self._display = self._display_widget.currentText + + self._updateComboBox(self._view_widget, self._config.getViews(self._display), self._view, ocio.view_default) + self._view = self._view_widget.currentText + + self._updateComboBox(self._swizzle_widget, ocio.SWIZZLE_TYPES, self._swizzle, ocio.swizzle_default) + self._swizzle = self._swizzle_widget.currentText + + self._updateGainWidgets() + + self._gamma_widget.setValue(self._gamma) + + #----------------------------------------------------------------------------------------- + + def _enableWidgets(self, enable): + self._select_config_action.setEnabled(enable) + self._select_lut_action.setEnabled(enable) + lut_enable = enable and not self._lut_file_list.isEmpty() + self._clear_lut_action.setEnabled(lut_enable) + self._lut_extrapolate_widget.setEnabled(lut_enable) + + self._color_space_widget.setEnabled(enable) + self._display_widget.setEnabled(enable) + self._view_widget.setEnabled(enable) + self._swizzle_widget.setEnabled(enable) + + self._fstop_widget.setEnabled(enable) + self._fstop_decrement_widget.setEnabled(enable) + self._fstop_increment_widget.setEnabled(enable) + self._gain_widget.setEnabled(enable) + self._exposure_widget.setEnabled(enable) + self._gain_reset_widget.setEnabled(enable) + + self._gamma_widget.setEnabled(enable) + self._gamma_reset_widget.setEnabled(enable) + + #----------------------------------------------------------------------------------------- + + def _addAction(self, identifier, command, icon_filename, tip, whats_this): + action = mari.actions.find(identifier) + if action is None: + action = mari.actions.create(identifier, command) + + icon_path = mari.resources.path(mari.resources.ICONS) + '/' + icon_filename + action.setIconPath(icon_path) + + action.setStatusTip(tip) + action.setToolTip(tip) + action.setWhatsThis(whats_this) + + return action + + #----------------------------------------------------------------------------------------- + + def _addWidgetGroup(self): + group_widget = QtGui.QWidget() + self._toolbar.addWidget(group_widget) + + layout = QtGui.QHBoxLayout() + layout.setSpacing(1) + layout.setMargin(1) + group_widget.setLayout(layout) + + return (group_widget, layout) + + #----------------------------------------------------------------------------------------- + + def _addComboBox(self, label, items, value, default, value_changed, *args): + group_widget, layout = self._addWidgetGroup() + + layout.addWidget(QtGui.QLabel(label)) + + widget = QtGui.QComboBox() + self._updateComboBox(widget, items, value, default) + widget.connect(QtCore.SIGNAL('currentIndexChanged(const QString &)'), value_changed) + layout.addWidget(widget) + + return widget + + #----------------------------------------------------------------------------------------- + + def _updateComboBox(self, widget, items, value, default): + block = widget.blockSignals(True) + + widget.clear() + for item in items: + widget.addItem(item) + + if items.count(value) != 0: + widget.setCurrentIndex(items.index(value)) + elif items.count(default) != 0: + widget.setCurrentIndex(items.index(default)) + + widget.blockSignals(block) + + #----------------------------------------------------------------------------------------- + + def _addSmallButtom(self, layout, label, tool_tip, value_changed, *args): + widget = QtGui.QPushButton(label); + widget.setToolTip(tool_tip); + widget.setFixedHeight(16); + widget.setFixedWidth(16); + widget.connect(QtCore.SIGNAL('released()'), value_changed) + layout.addWidget(widget); + + return widget + + #----------------------------------------------------------------------------------------- + + def _convertFStopWidgetValueToGain(self, value): + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.FSTOP_STEP_SIZE)) + scale = float(value) / float(widget_max) + exposure = ocio.EXPOSURE_MIN + scale * ocio.EXPOSURE_DELTA + return ocio.convertExposureToGain(exposure) + + #----------------------------------------------------------------------------------------- + + def _convertExposureWidgetValueToGain(self, value): + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.EXPOSURE_STEP_SIZE)) + scale = float(value) / float(widget_max) + exposure = ocio.EXPOSURE_MIN + scale * ocio.EXPOSURE_DELTA + return ocio.convertExposureToGain(exposure) + + #----------------------------------------------------------------------------------------- + + def _updateFStopWidgetText(self, fstop): + block = self._fstop_widget.blockSignals(True) + if fstop < 10.0: + # Floor the value to one decimal place and only display the decimal point if necessary + text = '%f' % fstop + index = text.index('.') + if text[index + 1] == '0': + text = text[:index] + else: + text = text[:index + 2] + self._fstop_widget.setText('f/%s' % text) + else: + self._fstop_widget.setText('f/%d' % int(fstop)) + self._fstop_widget.blockSignals(block) + + #----------------------------------------------------------------------------------------- + + def _updateGainWidgetText(self): + block = self._gain_widget.blockSignals(True) + self._gain_widget.setText(('%.' + ('%d' % ocio.GAIN_PRECISION) + 'f') % self._gain) + self._gain_widget.home(False) + self._gain_widget.blockSignals(block) + + #----------------------------------------------------------------------------------------- + + def _updateGainWidgets(self): + exposure = ocio.convertGainToExposure(self._gain) + fstop = ocio.convertExposureToFStop(exposure) + self._updateFStopWidgetText(fstop) + + scale = (exposure - ocio.EXPOSURE_MIN) / ocio.EXPOSURE_DELTA + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.FSTOP_STEP_SIZE)) + widget_value = int(round(scale * float(widget_max))) + block = self._fstop_widget.blockSignals(True) + self._fstop_widget.setValue(widget_value) + self._fstop_widget.blockSignals(block) + + self._updateGainWidgetText() + + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.EXPOSURE_STEP_SIZE)) + widget_value = int(round(scale * float(widget_max))) + block = self._gain_widget.blockSignals(True) + self._gain_widget.setValue(widget_value) + self._gain_widget.blockSignals(block) + block = self._exposure_widget.blockSignals(True) + self._exposure_widget.setValue(widget_value) + self._exposure_widget.blockSignals(block) + + #----------------------------------------------------------------------------------------- + + def _toggleColorManagement(self): + enabled = self.isColorManagementEnabled() + mari.gl_render.setPostProcessingEnabled(enabled) + self._enableWidgets(enabled) + ocio.printMessage(ocio.MessageType.DEBUG, 'Toggled color management to \'%s\'' % ('on' if enabled else 'off')) + + #----------------------------------------------------------------------------------------- + + def _clearLUT(self): + self.setLUTPath('') + ocio.printMessage(ocio.MessageType.DEBUG, 'Cleared lut') + + #----------------------------------------------------------------------------------------- + + def _fstopMovedMouse(self, value): + self.setGain(self._convertFStopWidgetValueToGain(float(value)), False) + + exposure = ocio.convertGainToExposure(self._gain) + fstop = ocio.convertExposureToFStop(exposure) + self._updateFStopWidgetText(fstop) + + self._updateGainWidgetText() + + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.EXPOSURE_STEP_SIZE)) + scale = (exposure - ocio.EXPOSURE_MIN) / ocio.EXPOSURE_DELTA + value = int(round(scale * float(widget_max))) + self._gain_widget.setValue(value) + + value = max(min(value, widget_max), 0) + self._exposure_widget.setValue(value) + + #----------------------------------------------------------------------------------------- + + def _gainMovedMouse(self, value): + self.setGain(self._convertExposureWidgetValueToGain(float(value)), False) + + self._updateGainWidgetText() + + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.EXPOSURE_STEP_SIZE)) + value = max(min(value, widget_max), 0) + self._exposure_widget.setValue(value) + + exposure = ocio.convertGainToExposure(self._gain) + fstop = ocio.convertExposureToFStop(exposure) + self._updateFStopWidgetText(fstop) + + scale = (exposure - ocio.EXPOSURE_MIN) / ocio.EXPOSURE_DELTA + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.FSTOP_STEP_SIZE)) + value = int(round(scale * float(widget_max))) + self._fstop_widget.setValue(value) + + #----------------------------------------------------------------------------------------- + + def _exposureChanged(self, value): + self.setGain(value = self._convertExposureWidgetValueToGain(float(value)), + update_widget = False, + update_metadata = True) + + self._updateGainWidgetText() + + self._gain_widget.setValue(value) + + exposure = ocio.convertGainToExposure(self._gain) + fstop = ocio.convertExposureToFStop(exposure) + self._updateFStopWidgetText(fstop) + + scale = (exposure - ocio.EXPOSURE_MIN) / ocio.EXPOSURE_DELTA + widget_max = int(math.ceil(ocio.EXPOSURE_DELTA / ocio.FSTOP_STEP_SIZE)) + value = int(round(scale * float(widget_max))) + self._fstop_widget.setValue(value) + + #----------------------------------------------------------------------------------------- + # Metadata: + #----------------------------------------------------------------------------------------- + + def _buildMetadata(self): + # LUT: + # --- + mari.utils.connect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + self._updateLUTMetadata() + + flags = self._lut_filter.METADATA_VISIBLE | self._lut_filter.METADATA_EDITABLE + self._lut_filter.setMetadataFlags('File', flags) + + self._lut_filter.setMetadataFlags('Extrapolate', flags) + + # Display: + # ------- + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + self._updateDisplayMetadata() + + self._display_filter.setMetadataDisplayName('ConfigPath', 'Configuration File') + flags = self._display_filter.METADATA_VISIBLE | self._display_filter.METADATA_EDITABLE + self._display_filter.setMetadataFlags('ConfigPath', flags) + + self._display_filter.setMetadataDisplayName('InputColorSpace', 'Input Color Space') + self._display_filter.setMetadataFlags('InputColorSpace', flags) + + self._display_filter.setMetadataDisplayName('Display', 'Display Device') + self._display_filter.setMetadataFlags('Display', flags) + + self._display_filter.setMetadataDisplayName('View', 'View Transform') + self._display_filter.setMetadataFlags('View', flags) + + self._display_filter.setMetadataDisplayName('Swizzle', 'Component') + self._display_filter.setMetadataFlags('Swizzle', flags) + + self._display_filter.setMetadataDefault('Gain', ocio.GAIN_RESET) + self._display_filter.setMetadataRange('Gain', ocio.GAIN_MIN, ocio.GAIN_MAX) + self._display_filter.setMetadataStep('Gain', ocio.GAIN_STEP_SIZE) + self._display_filter.setMetadataFlags('Gain', flags) + + self._display_filter.setMetadataDefault('Gamma', ocio.GAMMA_RESET) + self._display_filter.setMetadataRange('Gamma', ocio.GAMMA_MIN, ocio.GAMMA_MAX) + self._display_filter.setMetadataStep('Gamma', ocio.GAMMA_STEP_SIZE) + self._display_filter.setMetadataFlags('Gamma', flags) + + #----------------------------------------------------------------------------------------- + + def _updateLUTMetadata(self): + mari.utils.disconnect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + + self._lut_filter.setMetadata('File', self._lut_file_list) + + self._lut_filter.setMetadata('Extrapolate', self._lut_extrapolate) + + mari.utils.connect(self._lut_filter.metadataValueChanged, lutMetadataValueChanged) + + #----------------------------------------------------------------------------------------- + + def _updateDisplayMetadata(self): + mari.utils.disconnect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + self._display_filter.setMetadata('ConfigPath', self._config_file_list) + + color_spaces = [color_space.getName() for color_space in self._config.getColorSpaces()] + + self._display_filter.setMetadata('InputColorSpace', self._color_space) + self._display_filter.setMetadataItemList('InputColorSpace', color_spaces) + + self._display_filter.setMetadata('Display', self._display) + self._display_filter.setMetadataItemList('Display', self._config.getDisplays()) + + self._display_filter.setMetadata('View', self._view) + self._display_filter.setMetadataItemList('View', self._config.getViews(self._display)) + + self._display_filter.setMetadata('Swizzle', self._swizzle) + self._display_filter.setMetadataItemList('Swizzle', ocio.SWIZZLE_TYPES) + + self._display_filter.setMetadata('Gain', self._gain) + + self._display_filter.setMetadata('Gamma', self._gain) + + mari.utils.connect(self._display_filter.metadataValueChanged, displayMetadataValueChanged) + + + #----------------------------------------------------------------------------------------- + # External Connections: + #----------------------------------------------------------------------------------------- + + def _openedProject(self, project): + ocio.printMessage(ocio.MessageType.DEBUG, 'Loading settings for project \'%s\'' % project.name()) + + # Load the settings stored as metadata on the project... + + # General: + # ------- + + self._toggle_color_management_action.setEnabled(True) + self._toggle_color_management_action.setChecked(project.metadata('ColorEnabled') if project.hasMetadata('ColorEnabled') else ocio.enabled_default) + + # Enable/disable color management (MUST be done after modifications to 'self._toggle_color_management_action'. + mari.gl_render.setPostProcessingEnabled(self.isColorManagementEnabled()) + + filter_collection = None + if project.hasMetadata('ColorProfile'): + # *** IMPORTANT *** The post filter collection used to be called 'OpenColorIO' but was renamed to hide the + # fact we use OpenColorIO from our users. So as a temporary workaround we need to check for the old filter + # collection correct for it. + name = project.metadata('ColorProfile') + if name == 'OpenColorIO': + name = 'Color Space' + filter_collection = mari.gl_render.findPostFilterCollection(name) + + # Default the color management filter stack if the working one doesn't exist. + if filter_collection is None: + filter_collection = mari.gl_render.findPostFilterCollection(ocio.profile_default) + + mari.gl_render.setPostFilterCollection(filter_collection) + + # LUT: + # --- + + lut_extrapolate = project.metadata('OcioLutExtrapolate') if project.hasMetadata('OcioLutExtrapolate') else ocio.lut_extrapolate_default + force_shader_build = lut_extrapolate != self._lut_extrapolate + self._lut_extrapolate = lut_extrapolate + self._lut_extrapolate_widget.setChecked(self._lut_extrapolate) + + if project.hasMetadata('OcioLutPath'): + lut_path = ocio.buildLoadPath(project.metadata('OcioLutPath')) + if not self.setLUTPath(value = lut_path, update_metadata = True, force_shader_build = force_shader_build): + self.resetLUT() + else: + self.resetLUT() + + # Display: + # ------- + + self._color_space = project.metadata( 'OcioColorSpace') if project.hasMetadata('OcioColorSpace') else ocio.color_space_default + self._display = project.metadata( 'OcioDisplay') if project.hasMetadata( 'OcioDisplay') else ocio.display_default + self._view = project.metadata( 'OcioView') if project.hasMetadata( 'OcioView') else ocio.view_default + self._swizzle = project.metadata( 'OcioSwizzle') if project.hasMetadata( 'OcioSwizzle') else ocio.swizzle_default + self._gain = max(min(project.metadata('OcioGain'), + ocio.GAIN_MAX), + ocio.GAIN_MIN) if project.hasMetadata( 'OcioGain') else ocio.gain_default + self._gamma = project.metadata( 'OcioGamma') if project.hasMetadata( 'OcioGamma') else ocio.gamma_default + + # Attempt to load a configuration file... + self._config_file_list.clear() + self._config = None + + # 1. Environment variable. + config_path = os.getenv('OCIO') + if config_path is not None: + self.setConfigPath(config_path) + + # 2. Project setting. + if self._config is None and project.hasMetadata('OcioConfigPath'): + self.setConfigPath(ocio.buildLoadPath(project.metadata('OcioConfigPath'))) + + # 3. Use the default if nothing was found. + if self._config is None: + self._config_file_list = mari.FileList(ocio.config_file_list_default) + self._config = ocio.config_default + + self._updateDisplayWidgets() + self._rebuildDisplayFilter() + + self._enableWidgets(filter_collection.name() == 'Color Space' and self._toggle_color_management_action.isChecked()) + self._updateLUTMetadata() + self._updateDisplayMetadata() + + self._printLog() + + #----------------------------------------------------------------------------------------- + + def _aboutToSaveProject(self, project): + ocio.printMessage(ocio.MessageType.DEBUG, 'Saving settings for project \'%s\'' % project.name()) + + # Store the settings as metadata on the project. + project.setMetadata( 'ColorEnabled', self.isColorManagementEnabled()) + filter_collection = mari.gl_render.currentPostFilterCollection() + if filter_collection is not None: + project.setMetadata( 'ColorProfile', filter_collection.name()) + project.setMetadata('OcioLutExtrapolate', self._lut_extrapolate) + project.setMetadata( 'OcioLutPath', '' if self._lut_file_list.isEmpty() else ocio.buildSavePath(self._lut_file_list.at(0))) + if os.getenv('OCIO') is None: + project.setMetadata('OcioConfigPath', '' if self._config_file_list.isEmpty() else ocio.buildSavePath(self._config_file_list.at(0))) + project.setMetadata( 'OcioColorSpace', self._color_space) + project.setMetadata( 'OcioDisplay', self._display) + project.setMetadata( 'OcioView', self._view) + project.setMetadata( 'OcioSwizzle', self._swizzle) + project.setMetadata( 'OcioGain', self._gain) + project.setMetadata( 'OcioGamma', self._gamma) + + #----------------------------------------------------------------------------------------- + + def _closedProject(self): + self._toggle_color_management_action.setEnabled(False) + self._enableWidgets(False) + + #----------------------------------------------------------------------------------------- + + def _toolBarsCreated(self): + # Things like deleting Mari's configuration file and reseting the layout to the default will destroy the toolbar + # so we need to detect if this is the case and rebuild it! + toolbar = mari.app.findToolBar('Color Space') + if toolbar is None: + ocio.printMessage(ocio.MessageType.DEBUG, 'Rebuilding missing toolbar...') + self._buildWidgets() + + #----------------------------------------------------------------------------------------- + + def _postProcessingEnabled(self, enabled): + self._toggle_color_management_action.setChecked(enabled) + + # Only enable or disable UI if we have a current project. + current_project = mari.projects.current() + if current_project is not None: + self._enableWidgets(enabled) + + #----------------------------------------------------------------------------------------- + + def _setCurrentPostFilterCollection(self): + # Only enable or disable UI if we have a current project. + current_project = mari.projects.current() + if current_project is not None: + filter_collection = mari.gl_render.currentPostFilterCollection() + if filter_collection is None or filter_collection.name() != 'Color Space': + ocio.printMessage(ocio.MessageType.DEBUG, 'Disabling OpenColorIO') + self._enableWidgets(False) + else: + ocio.printMessage(ocio.MessageType.DEBUG, 'Enabling OpenColorIO') + self._enableWidgets(True) + + #----------------------------------------------------------------------------------------- + # Filter: + #----------------------------------------------------------------------------------------- + + def _rebuildLUTFilter(self, lut_path, force_shader_build = False): + if lut_path == '': + self._lut_filter.setDefinitionsSnippet('') + self._lut_filter.setBodySnippet('') + + if self._lut_sampler_name is not None: + self._lut_filter.deleteTexture(self._lut_sampler_name) + self._lut_sampler_name = None + + self._lut_filter_cache_id = None + self._lut_texture_cache_id = None + + else: + # There is a chance this is a bad file so we need to guard against it. + try: + self._lut_filter_cache_id, self._lut_texture_cache_id, self._lut_sampler_name = ocio.buildLUTFilter( + self._config, + lut_path, + self._lut_filter, + self._lut_filter_cache_id, + self._lut_texture_cache_id, + self._lut_extrapolate, + force_shader_build) + + except Exception, e: + message = 'Failed to load LUT file \'%s\' due to \'%s\'' % (lut_path, e) + ocio.printMessage(ocio.MessageType.ERROR, '%s' % message) + if not mari.app.inTerminalMode(): + mari.utils.misc.message(message, 'Color Space', 1024, 2) + + return False + + ocio.printMessage(ocio.MessageType.DEBUG, 'Changed LUT to \'%s\'' % lut_path) + + return True + + #----------------------------------------------------------------------------------------- + + def _rebuildDisplayFilter(self): + display_transform = ocio.PyOpenColorIO.DisplayTransform() + display_transform.setInputColorSpaceName(self._color_space) + + if hasattr(display_transform, 'setDisplay'): + # OCIO 1.0+ + display_transform.setDisplay(self._display) + display_transform.setView(self._view) + else: + # OCIO 0.8.X + display_color_space = self._config.getDisplayColorSpaceName(self._display, self._view) + display_transform.setDisplayColorSpaceName(display_color_space) + + # Add the channel sizzle. + luma_coefs = self._config.getDefaultLumaCoefs() + mtx, offset = ocio.PyOpenColorIO.MatrixTransform.View(ocio.SWIZZLE_VALUES[self._swizzle], luma_coefs) + + transform = ocio.PyOpenColorIO.MatrixTransform() + transform.setValue(mtx, offset) + display_transform.setChannelView(transform) + + # Add the linear gain. + transform = ocio.PyOpenColorIO.CDLTransform() + transform.setSlope((self._gain, self._gain, self._gain)) + display_transform.setLinearCC(transform) + + # Add the post-display CC. + transform = ocio.PyOpenColorIO.ExponentTransform() + transform.setValue([1.0 / max(1e-6, v) for v in (self._gamma, self._gamma, self._gamma, self._gamma)]) + display_transform.setDisplayCC(transform) + + processor = self._config.getProcessor(display_transform) + + self._display_filter_cache_id, self._display_texture_cache_id, self._display_sampler_name = ocio.buildProcessorFilter( + processor, + self._display_filter, + self._display_filter_cache_id, + self._display_texture_cache_id) + + current_canvas = mari.canvases.current() + if current_canvas is not None: + current_canvas.repaint() + + #----------------------------------------------------------------------------------------- + # Debugging: + #----------------------------------------------------------------------------------------- + + def _printLog(self): + ocio.printMessage( ocio.MessageType.INFO, '==============================================================') + ocio.printMessage( ocio.MessageType.INFO, 'Configuration:') + ocio.printMessage( ocio.MessageType.INFO, '==============================================================') + ocio.printMessage( ocio.MessageType.INFO, ' Enabled: %s; Default: %s' % (mari.gl_render.isPostProcessingEnabled(), + ocio.enabled_default)) + filter_collection = mari.gl_render.currentPostFilterCollection() + if filter_collection is not None: + ocio.printMessage(ocio.MessageType.INFO, ' Profile: %s; Default: %s' % (filter_collection.name(), + ocio.profile_default)) + else: + ocio.printMessage(ocio.MessageType.INFO, ' Profile: None; Default: %s' % (ocio.profile_default)) + ocio.printMessage( ocio.MessageType.INFO, ' LUT Path: %s; Default: %s' % ('' if self._lut_file_list.isEmpty() else self._lut_file_list.at(0), + '' if ocio.lut_file_list_default.isEmpty() else ocio.lut_file_list_default.at(0))) + ocio.printMessage( ocio.MessageType.INFO, ' Extrapolate: %s; Default: %s' % (self._lut_extrapolate, + ocio.lut_extrapolate_default)) + ocio.printMessage( ocio.MessageType.INFO, ' Config Path: %s; Default: %s' % ('' if self._config_file_list.isEmpty() else self._config_file_list.at(0), + '' if ocio.config_file_list_default.isEmpty() else ocio.config_file_list_default.at(0))) + ocio.printMessage( ocio.MessageType.INFO, ' Color Space: %s; Default: %s' % (self._color_space, + ocio.color_space_default)) + ocio.printMessage( ocio.MessageType.INFO, ' Display: %s; Default: %s' % (self._display, + ocio.display_default)) + ocio.printMessage( ocio.MessageType.INFO, ' View: %s; Default: %s' % (self._view, + ocio.view_default)) + ocio.printMessage( ocio.MessageType.INFO, ' Swizzle: %s; Default: %s' % (self._swizzle, + ocio.swizzle_default)) + ocio.printMessage( ocio.MessageType.INFO, ' F-Stop: %f; Default: %f; Center: %f' % (ocio.convertGainToFStop(self._gain), + ocio.convertGainToFStop(ocio.gain_default), + ocio.fstop_center)) + ocio.printMessage( ocio.MessageType.INFO, ' Gain: %f; Default: %f' % (self._gain, + ocio.gain_default)) + ocio.printMessage( ocio.MessageType.INFO, ' Gamma: %f; Default: %f' % (self._gamma, + ocio.gamma_default)) + ocio.printMessage( ocio.MessageType.INFO, '==============================================================') + +############################################################################################## +# The following functions CAN'T be part of the toolbar class as a potential bug in PythonQt +# causes the disconnect function to fail + +def lutMetadataValueChanged(name, value): + global toolbar + + ocio.printMessage(ocio.MessageType.DEBUG, 'LUT metadata \'%s\' changed to \'%s\'' % (name, value)) + + if name == 'File': + toolbar.setLUTPath(value = '' if value.isEmpty() else value.at(0), + update_metadata = False, + force_shader_build = False) + + elif name == 'Extrapolate': + toolbar.setExtrapolateEnabled(value = value, update_widget = True, update_metadata = False) + +#----------------------------------------------------------------------------------------- + +def displayMetadataValueChanged(name, value): + global toolbar + + ocio.printMessage(ocio.MessageType.DEBUG, 'Display metadata \'%s\' changed to \'%s\'' % (name, value)) + + if name == 'ConfigPath': + toolbar.setConfigPath(value = '' if value.isEmpty() else value.at(0), update_metadata = False) + + elif name == 'InputColorSpace': + toolbar.setColorSpace(value = value, update_widget = True, update_metadata = False) + + elif name == 'Display': + toolbar.setDisplay(value = value, update_widget = True, update_metadata = False) + + elif name == 'View': + toolbar.setView(value = value, update_widget = True, update_metadata = False) + + elif name == 'Swizzle': + toolbar.setSwizzle(value = value, update_widget = True, update_metadata = False) + + elif name == 'Gain': + toolbar.setGain(value = value, update_widget = True, update_metadata = False) + + elif name == 'Gamma': + toolbar.setGamma(value = value, update_widget = True, update_metadata = False) + +############################################################################################## + +if mari.app.isRunning(): + if not hasattr(mari.gl_render, 'createPostFilterCollection'): + ocio.printMessage(ocio.MessageType.ERROR, 'This version of Mari does not support the mari.gl_render.createPostFilterCollection API') + + else: + if ocio.config_default is not None: + toolbar = OcioToolBar() + + else: + # Destroy the OCIO post filter collection if present to prevent the user trying to use it. + filter_collection = mari.gl_render.findPostFilterCollection('Color Space') + if filter_collection is not None: + mari.gl_render.deletePostFilterCollection(filter_collection) + + # Destroy the toolbar to prevent the user trying to use it. + mari.app.deleteToolBar('Color Space') diff --git a/src/mari/1.4v1/ocio.py b/src/mari/1.4v1/ocio.py new file mode 100755 index 0000000..4f0f61e --- /dev/null +++ b/src/mari/1.4v1/ocio.py @@ -0,0 +1,789 @@ +#------------------------------------------------------------------------------- +# OpenColorIO (color management) related Mari scripts +# coding: utf-8 +# Copyright (c) 2011 The Foundry Visionmongers Ltd. All Rights Reserved. +#------------------------------------------------------------------------------- + +import mari, time, PythonQt, os, math + +############################################################################################## + +# Enable to output extended debugging messages. +VERBOSE_ENABLED = False + +# Message type identifiers. +class MessageType: + DEBUG = 1 + INFO = 2 + WARNING = 3 + ERROR = 4 + +def printMessage(type, message): + if type == MessageType.DEBUG: + if VERBOSE_ENABLED: + mari.app.log('[ OpenColorIO ] %s' % message) + elif type == MessageType.INFO: + mari.app.log('[ OpenColorIO ] %s' % message) + elif type == MessageType.WARNING: + mari.app.log('[ OpenColorIO ] [ WARNING ] %s' % message) + elif type == MessageType.ERROR: + mari.app.log('[ OpenColorIO ] [ ERROR ] %s' % message) + +############################################################################################## + +def configFileFilter(): + return 'OpenColorIO Configuration (*.ocio)' + +#--------------------------------------------------------------------------------------------- + +def lutFileFilter(): + result = 'All LUT Files (*.3dl *.ccc *.cc *.csp *.cub *.cube *.hdl *.m3d *.mga *.spi1d *.spi3d *.spimtx *.vf);;' + result += 'Autodesk LUT (*.3dl);;' + result += 'ASC CDL Color Correction Collection LUT (*.ccc);;' + result += 'ASC CDL Color Correction LUT (*.cc);;' + result += 'Cinespace LUT (*.csp);;' + result += 'Truelight LUT (*.cub);;' + result += 'Iridas LUT (*.cube);;' + result += 'Houdini LUT (*.hdl);;' + result += 'Pandora LUT (*.m3d *.mga);;' + result += 'Imageworks LUT (*.spi1d *.spi3d *.spimtx);;' + result += 'Inventor LUT (*.vf)' + return result + +############################################################################################## + +# Make sure the OpenColorIO python bindings are okay. +try: + import PyOpenColorIO + printMessage(MessageType.INFO, 'Loaded Python bindings \'%s\' successfully' % PyOpenColorIO.__file__) +except ImportError, e: + PyOpenColorIO = None + printMessage(MessageType.ERROR, 'Failed to load Python bindings \'%s\'' % e) + +LUT_FILE_LIST_RESET = mari.FileList(mari.FileList.TYPE_SINGLE_FILE) +LUT_FILE_LIST_RESET.setAcceptNonExisting(True) +LUT_FILE_LIST_RESET.setFilter(lutFileFilter()) + +CONFIG_FILE_LIST_RESET = mari.FileList(mari.FileList.TYPE_SINGLE_FILE) +if mari.app.isRunning(): + CONFIG_FILE_LIST_RESET.append(mari.resources.path(mari.resources.COLOR) + '/OpenColorIO/nuke-default/config.ocio') +else: + CONFIG_FILE_LIST_RESET.append('/OpenColorIO/nuke-default/config.ocio') +CONFIG_FILE_LIST_RESET.setPickedFile(CONFIG_FILE_LIST_RESET.at(0)) +CONFIG_FILE_LIST_RESET.setAcceptNonExisting(True) +CONFIG_FILE_LIST_RESET.setFilter(configFileFilter()) + +SQRT_TWO = math.sqrt(2.0) +LUT_SIZE_TYPES = ['Small', 'Medium', 'Large'] +LUT_SIZE_VALUES = {'Small': 16, + 'Medium': 32, + 'Large': 64} +LUT_SIZE_RESET = LUT_SIZE_TYPES[1] +ENABLED_RESET = True +PROFILE_RESET = 'Color Space' +LUT_EXTRAPOLATE_RESET = True +COLOR_SPACE_RESET = 'sRGB' +DISPLAY_RESET = 'default' +VIEW_RESET = 'sRGB' +SWIZZLE_TYPES = ['Luminance', 'RGB', 'R', 'G', 'B', 'A'] +SWIZZLE_VALUES = {'Luminance': ( True, True, True, False), + 'RGB': ( True, True, True, True), + 'R': ( True, False, False, False), + 'G': (False, True, False, False), + 'B': (False, False, True, False), + 'A': (False, False, False, True)} +SWIZZLE_RESET = SWIZZLE_TYPES[1] +FSTOP_STEP_SIZE = 0.5 +FSTOP_CENTER_MIN = 1.0 +FSTOP_CENTER_MAX = 64.0 +FSTOP_CENTER_STEP_SIZE = 0.001 +FSTOP_CENTER_RESET = 8.0 +EXPOSURE_MIN = -6.0 +EXPOSURE_MAX = +6.0 +EXPOSURE_DELTA = EXPOSURE_MAX - EXPOSURE_MIN +EXPOSURE_STEP_SIZE = 0.1 +GAIN_RESET = 1.0 +GAIN_MIN = 2.0 ** EXPOSURE_MIN +GAIN_MAX = 2.0 ** EXPOSURE_MAX +GAIN_STEP_SIZE = 0.000001 +GAIN_PRECISION = 6 +GAMMA_RESET = 1.0 +GAMMA_MIN = 0.0 +GAMMA_MAX = 4.0 +GAMMA_STEP_SIZE = 0.01 +GAMMA_PRECISION = 2 + +enabled_default = ENABLED_RESET +profile_default = PROFILE_RESET +lut_file_list_default = mari.FileList(LUT_FILE_LIST_RESET) +lut_extrapolate_default = LUT_EXTRAPOLATE_RESET +config_file_list_default = mari.FileList(CONFIG_FILE_LIST_RESET) +color_space_default = COLOR_SPACE_RESET +display_default = DISPLAY_RESET +view_default = VIEW_RESET +swizzle_default = SWIZZLE_RESET +gain_default = GAIN_RESET +gamma_default = GAMMA_RESET +lut_size = LUT_SIZE_RESET +fstop_center = FSTOP_CENTER_RESET + +config_default = None + +lut_size_functions = [] +fstop_center_functions = [] + +############################################################################################## + +def registerLUTSizeChanged(function): + global lut_size_functions + lut_size_functions.append(function) + +#--------------------------------------------------------------------------------------------- + +def registerFStopCenterChanged(function): + global fstop_center_functions + fstop_center_functions.append(function) + +#--------------------------------------------------------------------------------------------- + +def _enabledDefaultChanged(): + global enabled_default + enabled_default = mari.prefs.get('/Color/Color Management Defaults/colorEnabledDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _profileDefaultChanged(): + global profile_default + profile_default = mari.prefs.get('/Color/Color Management Defaults/colorProfileDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _postFilterCollectionAdded(filter_collection): + mari.prefs.setItemList('/Color/Color Management Defaults/colorProfileDefault', mari.gl_render.postFilterCollectionNames()) + +#--------------------------------------------------------------------------------------------- + +def _postFilterCollectionRemoved(name): + collection_names = mari.gl_render.postFilterCollectionNames() + mari.prefs.setItemList('/Color/Color Management Defaults/colorProfileDefault', collection_names) + + global PROFILE_RESET + if name == PROFILE_RESET: + PROFILE_RESET = collection_names[0] + mari.prefs.setDefault('/Color/Color Management Defaults/colorProfileDefault', PROFILE_RESET) + + global profile_default + if name == profile_default: + profile_default = PROFILE_RESET + mari.prefs.set('/Color/Color Management Defaults/colorProfileDefault', profile_default) + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _lutPathDefaultChanged(): + global lut_file_list_default + lut_file_list_default = mari.FileList(mari.prefs.get('/Color/LUT Defaults/lutPathDefault')) + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _lutExtrapolateDefaultChanged(): + global lut_extrapolate_default + lut_extrapolate_default = mari.prefs.get('/Color/LUT Defaults/lutExtrapolateDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _configPathDefaultChanged(): + global config_file_list_default + + # Only replace the existing configuration file if the new one is valid! + config_file_list = mari.FileList(mari.prefs.get('/Color/Display Defaults/displayConfigPathDefault')) + + if not config_file_list.isEmpty(): + config = loadConfig(config_file_list.at(0), True) + if config is not None: + config_file_list_default = config_file_list + + global config_default + config_default = config + + _updateColorSpaceDefault() + _updateDisplayDefault() + _updateViewDefault() + + _savePreferences() + + else: + # Put back the existing configuration file that works... + mari.prefs.set('/Color/Display Defaults/displayConfigPathDefault', config_file_list_default) + else: + # Put back the existing configuration file that works... + mari.prefs.set('/Color/Display Defaults/displayConfigPathDefault', config_file_list_default) + +#--------------------------------------------------------------------------------------------- + +def _colorSpaceDefaultChanged(): + global color_space_default + color_space_default = mari.prefs.get('/Color/Display Defaults/displayColorSpaceDefault') + _updateDisplayDefault() + _updateViewDefault() + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _displayDefaultChanged(): + global display_default + display_default = mari.prefs.get('/Color/Display Defaults/displayDisplayDefault') + _updateViewDefault() + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _viewDefaultChanged(): + global view_default + view_default = mari.prefs.get('/Color/Display Defaults/displayViewDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _swizzleDefaultChanged(): + global swizzle_default + swizzle_default = mari.prefs.get('/Color/Display Defaults/displaySwizzleDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _gainDefaultChanged(): + global gain_default + gain_default = mari.prefs.get('/Color/Display Defaults/displayGainDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _gammaDefaultChanged(): + global gamma_default + gamma_default = mari.prefs.get('/Color/Display Defaults/displayGammaDefault') + _savePreferences() + +#--------------------------------------------------------------------------------------------- + +def _lutSizeChanged(): + global lut_size + global lut_size_functions + lut_size = mari.prefs.get('/Color/Display General/displayLutSize') + _savePreferences() + for function in lut_size_functions: + function() + +#--------------------------------------------------------------------------------------------- + +def _fstopCenterChanged(): + global fstop_center + global fstop_center_functions + fstop_center = mari.prefs.get('/Color/Display General/displayFStopCenter') + _savePreferences() + for function in fstop_center_functions: + function() + +#--------------------------------------------------------------------------------------------- + +def _registerPreferences(): + global enabled_default + mari.prefs.set('/Color/Color Management Defaults/colorEnabledDefault', enabled_default) + mari.prefs.setChangedScript('/Color/Color Management Defaults/colorEnabledDefault', 'mari.utils.ocio._enabledDefaultChanged()') + mari.prefs.setDisplayName('/Color/Color Management Defaults/colorEnabledDefault', 'Enabled') + mari.prefs.setDefault('/Color/Color Management Defaults/colorEnabledDefault', ENABLED_RESET) + + global profile_default + mari.prefs.set('/Color/Color Management Defaults/colorProfileDefault', profile_default) + mari.prefs.setChangedScript('/Color/Color Management Defaults/colorProfileDefault', 'mari.utils.ocio._profileDefaultChanged()') + mari.prefs.setDisplayName('/Color/Color Management Defaults/colorProfileDefault', 'Color Profile') + mari.prefs.setDefault('/Color/Color Management Defaults/colorProfileDefault', PROFILE_RESET) + mari.prefs.setItemList('/Color/Color Management Defaults/colorProfileDefault', mari.gl_render.postFilterCollectionNames()) + + global lut_file_list_default + if not lut_file_list_default.isEmpty() and not os.path.isfile(lut_file_list_default.at(0)): + message = 'LUT file \'%s\' does not exist' % lut_file_list_default.at(0) + printMessage(MessageType.ERROR, '%s' % message) + lut_file_list_default = mari.FileList(LUT_FILE_LIST_RESET) + + mari.prefs.set('/Color/LUT Defaults/lutPathDefault', lut_file_list_default) + mari.prefs.setChangedScript('/Color/LUT Defaults/lutPathDefault', 'mari.utils.ocio._lutPathDefaultChanged()') + mari.prefs.setDisplayName('/Color/LUT Defaults/lutPathDefault', 'File') + mari.prefs.setDefault('/Color/LUT Defaults/lutPathDefault', LUT_FILE_LIST_RESET) + + global lut_extrapolate_default + mari.prefs.set('/Color/LUT Defaults/lutExtrapolateDefault', lut_extrapolate_default) + mari.prefs.setChangedScript('/Color/LUT Defaults/lutExtrapolateDefault', 'mari.utils.ocio._lutExtrapolateDefaultChanged()') + mari.prefs.setDisplayName('/Color/LUT Defaults/lutExtrapolateDefault', 'Extrapolate') + mari.prefs.setDefault('/Color/LUT Defaults/lutExtrapolateDefault', LUT_EXTRAPOLATE_RESET) + + global config_file_list_default + global config_default + if not config_file_list_default.isEmpty(): + config = loadConfig(config_file_list_default.at(0), False) + if config is not None: + config_default = config + else: + config_file_list_default = mari.FileList(CONFIG_FILE_LIST_RESET) + else: + config_file_list_default = mari.FileList(CONFIG_FILE_LIST_RESET) + + mari.prefs.set('/Color/Display Defaults/displayConfigPathDefault', config_file_list_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displayConfigPathDefault', 'mari.utils.ocio._configPathDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displayConfigPathDefault', 'Configuration File') + mari.prefs.setDefault('/Color/Display Defaults/displayConfigPathDefault', CONFIG_FILE_LIST_RESET) + + color_spaces = [color_space.getName() for color_space in config_default.getColorSpaces()] + + color_space_reset = COLOR_SPACE_RESET + if color_spaces.count(color_space_reset) == 0: + color_space_reset = color_spaces[0] + + global color_space_default + if color_spaces.count(color_space_default) == 0: + color_space_default = color_space_reset + + mari.prefs.set('/Color/Display Defaults/displayColorSpaceDefault', color_space_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displayColorSpaceDefault', 'mari.utils.ocio._colorSpaceDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displayColorSpaceDefault', 'Input Color Space') + mari.prefs.setDefault('/Color/Display Defaults/displayColorSpaceDefault', color_space_reset) + mari.prefs.setItemList('/Color/Display Defaults/displayColorSpaceDefault', color_spaces) + + displays = config_default.getDisplays() + + display_reset = DISPLAY_RESET + if displays.count(display_reset) == 0: + display_reset = config_default.getDefaultDisplay() + + global display_default + if displays.count(display_default) == 0: + display_default = display_reset + + mari.prefs.set('/Color/Display Defaults/displayDisplayDefault', display_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displayDisplayDefault', 'mari.utils.ocio._displayDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displayDisplayDefault', 'Display') + mari.prefs.setDefault('/Color/Display Defaults/displayDisplayDefault', display_reset) + mari.prefs.setItemList('/Color/Display Defaults/displayDisplayDefault', displays) + + views = config_default.getViews(display_default) + + view_reset = VIEW_RESET + if views.count(view_reset) == 0: + view_reset = config_default.getDefaultView(display_default) + + global view_default + if views.count(view_default) == 0: + view_default = view_reset + + mari.prefs.set('/Color/Display Defaults/displayViewDefault', view_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displayViewDefault', 'mari.utils.ocio._viewDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displayViewDefault', 'View') + mari.prefs.setDefault('/Color/Display Defaults/displayViewDefault', view_reset) + mari.prefs.setItemList('/Color/Display Defaults/displayViewDefault', views) + + global swizzle_default + if SWIZZLE_TYPES.count(swizzle_default) == 0: + swizzle_default = SWIZZLE_RESET + + mari.prefs.set('/Color/Display Defaults/displaySwizzleDefault', swizzle_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displaySwizzleDefault', 'mari.utils.ocio._swizzleDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displaySwizzleDefault', 'Component') + mari.prefs.setDefault('/Color/Display Defaults/displaySwizzleDefault', SWIZZLE_RESET) + mari.prefs.setItemList('/Color/Display Defaults/displaySwizzleDefault', SWIZZLE_TYPES) + + global gain_default + mari.prefs.set('/Color/Display Defaults/displayGainDefault', gain_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displayGainDefault', 'mari.utils.ocio._gainDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displayGainDefault', 'Gain') + mari.prefs.setDefault('/Color/Display Defaults/displayGainDefault', GAIN_RESET) + mari.prefs.setRange('/Color/Display Defaults/displayGainDefault', GAIN_MIN, GAIN_MAX) + mari.prefs.setStep('/Color/Display Defaults/displayGainDefault', GAIN_STEP_SIZE) + + global gamma_default + mari.prefs.set('/Color/Display Defaults/displayGammaDefault', gamma_default) + mari.prefs.setChangedScript('/Color/Display Defaults/displayGammaDefault', 'mari.utils.ocio._gammaDefaultChanged()') + mari.prefs.setDisplayName('/Color/Display Defaults/displayGammaDefault', 'Gamma') + mari.prefs.setDefault('/Color/Display Defaults/displayGammaDefault', GAMMA_RESET) + mari.prefs.setRange('/Color/Display Defaults/displayGammaDefault', GAMMA_MIN, GAMMA_MAX) + mari.prefs.setStep('/Color/Display Defaults/displayGammaDefault', GAMMA_STEP_SIZE) + + global lut_size + mari.prefs.set('/Color/Display General/displayLutSize', lut_size) + mari.prefs.setChangedScript('/Color/Display General/displayLutSize', 'mari.utils.ocio._lutSizeChanged()') + mari.prefs.setDisplayName('/Color/Display General/displayLutSize', 'LUT Size') + mari.prefs.setDefault('/Color/Display General/displayLutSize', LUT_SIZE_RESET) + mari.prefs.setItemList('/Color/Display General/displayLutSize', LUT_SIZE_TYPES) + + global fstop_center + mari.prefs.set('/Color/Display General/displayFStopCenter', fstop_center) + mari.prefs.setChangedScript('/Color/Display General/displayFStopCenter', 'mari.utils.ocio._fstopCenterChanged()') + mari.prefs.setDisplayName('/Color/Display General/displayFStopCenter', 'Center F-Stop') + mari.prefs.setDefault('/Color/Display General/displayFStopCenter', FSTOP_CENTER_RESET) + mari.prefs.setRange('/Color/Display General/displayFStopCenter', FSTOP_CENTER_MIN, FSTOP_CENTER_MAX) + mari.prefs.setStep('/Color/Display General/displayFStopCenter', FSTOP_CENTER_STEP_SIZE) + + # Attach ourselves to the appropriate project signals so we can update widgets. + PythonQt.QtCore.QObject.connect(mari.gl_render.postFilterCollectionAdded.__self__, + mari.gl_render.postFilterCollectionAdded.__name__, + _postFilterCollectionAdded) + PythonQt.QtCore.QObject.connect(mari.gl_render.postFilterCollectionRemoved.__self__, + mari.gl_render.postFilterCollectionRemoved.__name__, + _postFilterCollectionRemoved) + +#--------------------------------------------------------------------------------------------- + +def _updateColorSpaceDefault(): + global config_default + global color_space_default + + color_spaces = [color_space.getName() for color_space in config_default.getColorSpaces()] + + color_space_reset = COLOR_SPACE_RESET + if color_spaces.count(color_space_reset) == 0: + color_space_reset = color_spaces[0] + + if color_spaces.count(color_space_default) == 0: + color_space_default = color_space_reset + + mari.prefs.setItemList('/Color/Display Defaults/displayColorSpaceDefault', color_spaces) + mari.prefs.set('/Color/Display Defaults/displayColorSpaceDefault', color_space_default) + mari.prefs.setDefault('/Color/Display Defaults/displayColorSpaceDefault', color_space_reset) + + +#--------------------------------------------------------------------------------------------- + +def _updateDisplayDefault(): + global config_default + global display_default + + displays = config_default.getDisplays() + + display_reset = DISPLAY_RESET + if displays.count(display_reset) == 0: + display_reset = config_default.getDefaultDisplay() + + if displays.count(display_default) == 0: + display_default = display_reset + + mari.prefs.setItemList('/Color/Display Defaults/displayDisplayDefault', displays) + mari.prefs.set('/Color/Display Defaults/displayDisplayDefault', display_default) + mari.prefs.setDefault('/Color/Display Defaults/displayDisplayDefault', display_reset) + +#--------------------------------------------------------------------------------------------- + +def _updateViewDefault(): + global config_default + global display_default + global view_default + + views = config_default.getViews(display_default) + + view_reset = VIEW_RESET + if views.count(view_reset) == 0: + view_reset = config_default.getDefaultView(display_default) + + if views.count(view_default) == 0: + view_default = view_reset + + mari.prefs.setItemList('/Color/Display Defaults/displayViewDefault', views) + mari.prefs.set('/Color/Display Defaults/displayViewDefault', view_default) + mari.prefs.setDefault('/Color/Display Defaults/displayViewDefault', view_reset) + +#--------------------------------------------------------------------------------------------- + +def _loadPreferences(): + settings = mari.Settings() + settings.beginGroup('OpenColorIO') + + try: + global enabled_default + global profile_default + global lut_file_list_default + global lut_extrapolate_default + global config_file_list_default + global color_space_default + global display_default + global view_default + global swizzle_default + global gain_default + global gamma_default + global lut_size + global fstop_center + + enabled_default = False if int(settings.value('enabledDefault', ENABLED_RESET)) == 0 else True + profile_default = str(settings.value('profileDefault', PROFILE_RESET)) + lut_path = buildLoadPath(str(settings.value('lutPathDefault', '' if LUT_FILE_LIST_RESET.isEmpty() else LUT_FILE_LIST_RESET.at(0)))) + lut_extrapolate_default = False if int(settings.value('lutExtrapolateDefault', LUT_EXTRAPOLATE_RESET)) == 0 else True + config_path = buildLoadPath(str(settings.value('configPathDefault', '' if CONFIG_FILE_LIST_RESET.isEmpty() else CONFIG_FILE_LIST_RESET.at(0)))) + color_space_default = str(settings.value('colorSpaceDefault', COLOR_SPACE_RESET)) + display_default = str(settings.value('displayDefault', DISPLAY_RESET)) + view_default = str(settings.value('viewDefault', VIEW_RESET)) + swizzle_default = str(settings.value('swizzleDefault', SWIZZLE_RESET)) + gain_default = max(min(float(settings.value('gainDefault', GAIN_RESET)), GAIN_MAX), GAIN_MIN) + gamma_default = max(min(float(settings.value('gammaDefault', GAMMA_RESET)), GAMMA_MAX), GAMMA_MIN) + lut_size = str(settings.value('lutSize', LUT_SIZE_RESET)) + fstop_center = max(min(float(settings.value('fstopCenter', FSTOP_CENTER_RESET)), FSTOP_CENTER_MAX), FSTOP_CENTER_MIN) + + if os.path.isfile(lut_path): + lut_file_list_default.clear() + lut_file_list_default.append(lut_path) + lut_file_list_default.setPickedFile(lut_path) + + if os.path.isfile(config_path): + config_file_list_default.clear() + config_file_list_default.append(config_path) + config_file_list_default.setPickedFile(config_path) + + except ValueError, e: + printMessage(MessageType.ERROR, 'Failed to load preferences \'%s\'' % e) + + settings.endGroup() + + _printPreferences(MessageType.DEBUG, 'Loaded Preferences:') + +#--------------------------------------------------------------------------------------------- + +def _savePreferences(): + settings = mari.Settings() + settings.beginGroup('OpenColorIO') + + global enabled_default + global profile_default + global lut_file_list_default + global lut_extrapolate_default + global config_file_list_default + global color_space_default + global display_default + global view_default + global swizzle_default + global gain_default + global gamma_default + global lut_size + global fstop_center + + settings.setValue( 'enabledDefault', 1 if enabled_default else 0) + settings.setValue( 'profileDefault', profile_default) + settings.setValue( 'lutPathDefault', '' if lut_file_list_default.isEmpty() else buildSavePath(lut_file_list_default.at(0))) + settings.setValue('lutExtrapolateDefault', 1 if lut_extrapolate_default else 0) + settings.setValue( 'configPathDefault', '' if config_file_list_default.isEmpty() else buildSavePath(config_file_list_default.at(0))) + settings.setValue( 'colorSpaceDefault', color_space_default) + settings.setValue( 'displayDefault', display_default) + settings.setValue( 'viewDefault', view_default) + settings.setValue( 'swizzleDefault', swizzle_default) + settings.setValue( 'gainDefault', gain_default) + settings.setValue( 'gammaDefault', gamma_default) + settings.setValue( 'lutSize', lut_size) + settings.setValue( 'fstopCenter', fstop_center) + + settings.endGroup() + + _printPreferences(MessageType.DEBUG, 'Saved Preferences:') + +#--------------------------------------------------------------------------------------------- + +def _printPreferences(type, title): + global enabled_default + global profile_default + global lut_file_list_default + global lut_extrapolate_default + global config_file_list_default + global color_space_default + global display_default + global view_default + global swizzle_default + global gain_default + global fstop_center + global lut_size + global gamma_default + + printMessage(type, '==============================================================') + printMessage(type, title) + printMessage(type, '==============================================================') + printMessage(type, ' Enabled: %s' % enabled_default) + printMessage(type, ' Profile: %s' % profile_default) + printMessage(type, ' LUT Path: %s' % '' if lut_file_list_default.isEmpty() else lut_file_list_default.at(0)) + printMessage(type, ' Extrapolate: %s' % lut_extrapolate_default) + printMessage(type, ' Config Path: %s' % '' if config_file_list_default.isEmpty() else config_file_list_default.at(0)) + printMessage(type, ' Color Space: %s' % color_space_default) + printMessage(type, ' Display: %s' % display_default) + printMessage(type, ' View: %s' % view_default) + printMessage(type, ' Swizzle: %s' % swizzle_default) + printMessage(type, ' F-Stop: %f; Center: %f' % (convertGainToFStop(gain_default), fstop_center)) + printMessage(type, ' Gain: %f' % gain_default) + printMessage(type, ' Gamma: %f' % gamma_default) + printMessage(type, ' LUT Size: %s' % lut_size) + printMessage(type, '==============================================================') + +############################################################################################## + +def convertExposureToGain(exposure): + return 2.0 ** exposure + +#--------------------------------------------------------------------------------------------- + +def convertGainToExposure(gain): + return math.log(gain, 2.0) + +#--------------------------------------------------------------------------------------------- + +def convertExposureToFStop(exposure): + global fstop_center + exposure_center = math.log(fstop_center, SQRT_TWO) + return math.pow(SQRT_TWO, exposure_center - exposure) + +#--------------------------------------------------------------------------------------------- + +def convertGainToFStop(gain): + exposure = convertGainToExposure(gain) + return convertExposureToFStop(exposure) + +#--------------------------------------------------------------------------------------------- + +def buildProcessorFilter(processor, filter, filter_cache_id, texture_cache_id, extrapolate = False, force_shader_build = False): + # Create a name, using the filter's name, that can be used in uniquely naming parameters and functions. + name = filter.name(); + name = name.lower() + name = name.replace(' ', '_') + + sampler_name = 'ocio_' + name + '_lut_$ID_' + function_name = 'ocio_' + name + '_$ID_' + + global lut_size + shader_desc = { 'language': PyOpenColorIO.Constants.GPU_LANGUAGE_GLSL_1_3, + 'functionName': function_name, + 'lut3DEdgeLen': LUT_SIZE_VALUES[lut_size]} + + cache_id = processor.getGpuShaderTextCacheID(shader_desc) + if cache_id != filter_cache_id or force_shader_build: + filter_cache_id = cache_id + printMessage(MessageType.DEBUG, 'Creating new GLSL filter...') + + desc = 'uniform sampler3D ' + sampler_name + ';\n' + desc += processor.getGpuShaderText(shader_desc) + body = '' + if extrapolate: + # The following code was taken from Nuke's 'LUT3D::Extrapolate' functionality. It attempts to estimate what + # the corresponding color value would be when the incoming color value is outside the normal range of [0-1], + # such as the case with HDR images. + rcp_lut_edge_length = 1.0 / float(LUT_SIZE_VALUES[lut_size]) + + body += '{\n' + body += ' if( 1.0 < Out.r || 1.0 < Out.g || 1.0 < Out.b )\n' + body += ' {\n' + body += ' vec4 closest;\n' + body += ' closest.rgb = clamp(Out.rgb, vec3(0.0), vec3(1.0));\n' + body += ' closest.a = Out.a;\n' + body += '\n' + body += ' vec3 offset = Out.rgb - closest.rgb;\n' + body += ' float offset_distance = length(offset);\n' + body += ' offset = normalize(offset);\n' + body += '\n' + body += ' vec4 nbr_position;\n' + body += ' nbr_position.rgb = closest.rgb - %f * offset;\n' % rcp_lut_edge_length + body += ' nbr_position.a = Out.a;\n' + body += '\n' + body += ' Out = ' + function_name + '(closest, ' + sampler_name + ');\n' + body += ' Out.rgb += (Out.rgb - ' + function_name + '(nbr_position, ' + sampler_name + ').rgb) / %f * offset_distance;\n' % rcp_lut_edge_length + body += ' }\n' + body += ' else\n' + body += ' {\n' + body += ' Out = ' + function_name + '(Out, ' + sampler_name + ');\n' + body += ' }\n' + body += '}\n' + else: + body += '{ Out = ' + function_name + '(Out, ' + sampler_name + '); }\n' + + filter.setDefinitionsSnippet(desc) + filter.setBodySnippet(body) + else: + printMessage(MessageType.DEBUG, 'No GLSL filter update required') + + cache_id = processor.getGpuLut3DCacheID(shader_desc) + if cache_id != texture_cache_id: + lut = processor.getGpuLut3D(shader_desc) + + printMessage(MessageType.DEBUG, 'Updating LUT...') + + if texture_cache_id is None: + filter.setTexture3D(sampler_name, + LUT_SIZE_VALUES[lut_size], + LUT_SIZE_VALUES[lut_size], + LUT_SIZE_VALUES[lut_size], + filter.FORMAT_RGB, + lut) + else: + filter.updateTexture3D(sampler_name, lut) + + texture_cache_id = cache_id + else: + printMessage(MessageType.DEBUG, 'No LUT update required') + + return (filter_cache_id, texture_cache_id, sampler_name) + +#--------------------------------------------------------------------------------------------- + +def buildLUTFilter(config, path, filter, filter_cache_id, texture_cache_id, extrapolate, force_shader_build = False): + file_transform = PyOpenColorIO.FileTransform() + file_transform.setSrc(path) + file_transform.setInterpolation('linear') + + processor = config.getProcessor(file_transform) + + return buildProcessorFilter(processor, filter, filter_cache_id, texture_cache_id, extrapolate, force_shader_build) + +#--------------------------------------------------------------------------------------------- + +def loadConfig(path, display_message_box = True): + try: + config = PyOpenColorIO.Config.CreateFromFile(path) + return config + + except Exception, e: + message = 'Failed to load configuration file \'%s\' due to \'%s\'' % (path, e) + printMessage(MessageType.ERROR, '%s' % message) + if display_message_box and not mari.app.inTerminalMode(): + mari.utils.misc.message(message, 'Color Space', 1024, 2) + + return None + +#--------------------------------------------------------------------------------------------- + +# This converts a path into a form which can be shared among different platforms and installations. +def buildSavePath(path): + result = path.replace(mari.resources.path(mari.resources.COLOR), '$MARI_COLOR_PATH', 1) + return result + +#--------------------------------------------------------------------------------------------- + +# This converts a path saved out to disk back into a form which can used by the application. +def buildLoadPath(path): + result = path.replace('$MARI_COLOR_PATH', mari.resources.path(mari.resources.COLOR), 1) + return result + +############################################################################################## + +if mari.app.isRunning(): + if PyOpenColorIO is not None: + # Attempt to load the default configuration file... without it we can't do nothing! + config_file_lists = [config_file_list_default, CONFIG_FILE_LIST_RESET] + for config_file_list in config_file_lists: + if not config_file_list.isEmpty(): + config_default = loadConfig(config_file_list.at(0), False) + if config_default is not None: + config_file_list_default = mari.FileList(config_file_list) + break + + if config_default is not None: + _loadPreferences() + _registerPreferences() + _savePreferences() + + else: + message = 'Failed to find a working configuration file. OpenColorIO will be disabled!' + printMessage(MessageType.ERROR, message) + if not mari.app.inTerminalMode(): + mari.utils.misc.message(message, 'OpenColorIO', 1024, 3) diff --git a/src/mari/prototype/README b/src/mari/prototype/README new file mode 100644 index 0000000..514cd65 --- /dev/null +++ b/src/mari/prototype/README @@ -0,0 +1,3 @@ +This is a prototype implemetation for a python display integration, which worked +in early Mari versions. It's provided as a simple example of how to use the +OCIO python API to query the GPU interface functions. diff --git a/src/mari/prototype/ociodisplay.py b/src/mari/prototype/ociodisplay.py new file mode 100644 index 0000000..5b0b949 --- /dev/null +++ b/src/mari/prototype/ociodisplay.py @@ -0,0 +1,242 @@ +""" +This script allows the use of OpenColorIO display transforms (3d luts) in +the Mari Viewer. Requires Mari 1.3v2+. + +This example is not represntative of the final Mari OCIO workflow, merely +an API demonstration. This code is a work in progress, to demonstrate the +integration of OpenColorIO and Mari. The APIs this code relies on are subject +to change at any time, and as such should not be relied on for production use +(yet). + +LINUX testing instructions: + +* Build OCIO +mkdir -p dist_mari +mkdir -p build_mari && cd build_mari +cmake -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_PREFIX=../dist_mari \ + -D PYTHON=/usr/bin/python2.6 \ + -D OCIO_NAMESPACE=OpenColorIO_Mari \ + ../ +make install -j8 + +* Set $OCIO color environment +setenv OCIO setenv OCIO <YOURDIR>/ocio.configs/spi-vfx/config.ocio +(Profiles available for download from opencolorio.org) + +* Run Mari with OpenColorIO added to the LD_LIBRARY_PATH, and Python +env LD_LIBRARY_PATH=<YOURDIR>/dist_mari/lib/ PYTHONPATH=<YOURDIR>/dist_mari/lib/python2.6 mari + +* Source this script in the python console. +Also - IMPORTANT - you must enable 'Use Color Correction' in the Color Manager. + +""" + +import mari, time, PythonQt +QtGui = PythonQt.QtGui +QtCore = PythonQt.QtCore + +try: + import PyOpenColorIO as OCIO + mari.app.log("OCIODisplay: %s" % OCIO.__file__) +except Exception,e: + OCIO = None + mari.app.log("OCIODisplay: Error: Could not import OpenColorIO python bindings.") + mari.app.log("OCIODisplay: Please confirm PYTHONPATH has dir containing PyOpenColorIO.so") + +__all__ = [ 'OCIO', 'CreateOCIODisplayTransform', 'OCIODisplayUI'] + +LUT3D_SIZE = 32 +WINDOW_NAME = "OpenColorIO Display Manager" +CREATE_FLOATING = True + + +class OCIODisplayUI(QtGui.QWidget): + def __init__(self): + QtGui.QWidget.__init__(self) + QtGui.QGridLayout(self) + self.setWindowTitle(WINDOW_NAME) + self.setMinimumWidth(300) + + config = OCIO.GetCurrentConfig() + + self.__inputColorSpace = OCIO.Constants.ROLE_DEFAULT + inputColorSpaces = [ OCIO.Constants.ROLE_TEXTURE_PAINT, + 'dt8', + OCIO.Constants.ROLE_SCENE_LINEAR ] + for cs in inputColorSpaces: + if config.getColorSpace(cs) is None: continue + self.__inputColorSpace = cs + break + + self.__fStopOffset = 0.0 + self.__viewGamma = 1.0 + self.__swizzle = (True, True, True, True) + + self.__filter_cacheID = None + self.__filter = None + self.__texture3d_cacheID = None + self.__counter_hack = 0 + + self.__buildUI() + self.__rebuildFilter() + + def __buildUI(self): + config = OCIO.GetCurrentConfig() + + self.layout().addWidget( QtGui.QLabel("Input Color Space", self), 0, 0) + csWidget = QtGui.QComboBox(self) + self.layout().addWidget( csWidget, 0, 1) + csIndex = None + for name in (cs.getName() for cs in config.getColorSpaces()): + csWidget.addItem(name) + if name == self.__inputColorSpace: + csIndex = csWidget.count - 1 + if csIndex is not None: + csWidget.setCurrentIndex(csIndex) + csWidget.connect( QtCore.SIGNAL('currentIndexChanged(const QString &)'), self.__csChanged) + + + # This doesnt work until the Horizontal enum is exposed. + """ + self.__fstopWidget_numStops = 3.0 + self.__fstopWidget_ticksPerStop = 4 + + self.layout().addWidget( QtGui.QLabel("FStop", self), 1, 0) + fstopWidget = QtGui.QSlider(Horizontal, self) + self.layout().addWidget( fstopWidget, 1, 1) + fstopWidget.setMinimum(int(-self.__fstopWidget_numStops*self.__fstopWidget_ticksPerStop)) + fstopWidget.setMaximum(int(self.__fstopWidget_numStops*self.__fstopWidget_ticksPerStop)) + fstopWidget.setTickInterval(self.__fstopWidget_ticksPerStop) + """ + + + def __csChanged(self, text): + text = str(text) + if text != self.__inputColorSpace: + self.__inputColorSpace = text + self.__rebuildFilter() + + def __rebuildFilter(self): + config = OCIO.GetCurrentConfig() + display = config.getDefaultDisplay() + view = config.getDefaultView(display) + transform = CreateOCIODisplayTransform(config, self.__inputColorSpace, + display, view, + self.__swizzle, + self.__fStopOffset, self.__viewGamma) + + processor = config.getProcessor(transform) + + shaderDesc = dict( [('language', OCIO.Constants.GPU_LANGUAGE_GLSL_1_3), + ('functionName', 'display_ocio_$ID_'), + ('lut3DEdgeLen', LUT3D_SIZE)] ) + + filterCacheID = processor.getGpuShaderTextCacheID(shaderDesc) + if filterCacheID != self.__filter_cacheID: + self.__filter_cacheID = filterCacheID + mari.app.log("OCIODisplay: Creating filter %s" % self.__filter_cacheID) + + desc = "sampler3D lut3d_ocio_$ID_;\n" + desc += processor.getGpuShaderText(shaderDesc) + body = "{ Out = display_ocio_$ID_(Out, lut3d_ocio_$ID_); }" + + # Clear the existing color managment filter stack and create a new filter + # HACK: Increment a counter by 1 each time to force a refresh + #self.__counter_hack += 1 + #name = "OCIO %s %s %s v%d" % (display, view, self.__inputColorSpace, self.__counter_hack) + name = "OCIO %s %s %s" % (display, view, self.__inputColorSpace) + + self.__filter = None + self.__texture3d_cacheID = None + + mari.gl_render.clearPostFilterStack() + self.__filter = mari.gl_render.createPostFilter(name, desc, body) + mari.gl_render.appendPostFilter(self.__filter) + else: + mari.app.log('OCIODisplay: no shader text update required') + + texture3d_cacheID = processor.getGpuLut3DCacheID(shaderDesc) + if texture3d_cacheID != self.__texture3d_cacheID: + lut3d = processor.getGpuLut3D(shaderDesc) + + mari.app.log("OCIODisplay: Updating 3dlut %s" % texture3d_cacheID) + + if self.__texture3d_cacheID is None: + self.__filter.setTexture3D("lut3d_ocio_$ID_", + LUT3D_SIZE, LUT3D_SIZE, LUT3D_SIZE, + self.__filter.FORMAT_RGB, lut3d) + else: + self.__filter.updateTexture3D( "lut3d_ocio_$ID_", lut3d) + + self.__texture3d_cacheID = texture3d_cacheID + else: + mari.app.log("OCIODisplay: No lut3d update required") + + + +def CreateOCIODisplayTransform(config, + inputColorSpace, + display, view, + swizzle, + fstopOffset, viewGamma): + + displayTransform = OCIO.DisplayTransform() + displayTransform.setInputColorSpaceName( inputColorSpace ) + + displayColorSpace = config.getDisplayColorSpaceName(display, view) + displayTransform.setDisplayColorSpaceName( displayColorSpace ) + + # Add the channel sizzle + lumacoef = config.getDefaultLumaCoefs() + mtx, offset = OCIO.MatrixTransform.View(swizzle, lumacoef) + + transform = OCIO.MatrixTransform() + transform.setValue(mtx, offset) + displayTransform.setChannelView(transform) + + # Add the linear fstop gain + gain = 2**fstopOffset + mtx, offset = OCIO.MatrixTransform.Scale((gain,gain,gain,gain)) + transform = OCIO.MatrixTransform() + transform.setValue(mtx, offset) + displayTransform.setLinearCC(transform) + + # Add the post-display CC + transform = OCIO.ExponentTransform() + transform.setValue([1.0 / max(1e-6, v) for v in \ + (viewGamma, viewGamma, viewGamma, viewGamma)]) + displayTransform.setDisplayCC(transform) + + return displayTransform + +""" +SWIZZLE_COLOR = (True, True, True, True) +SWIZZLE_RED = (True, False, False, False) +SWIZZLE_GREEN = (False, True, False, False) +SWIZZLE_BLUE = (False, False, True, False) +SWIZZLE_ALPHA = (False, False, False, True) +SWIZZLE_LUMA = (True, True, True, False) + +Timings + +OCIO Setup: 0.5 ms +OCIO 3D Lut creation: 14.7 ms +Mari Setup: 21.3 ms +Mari Texture Upload: 44.2 ms +""" + + +if __name__ == '__main__': + if not hasattr(mari.gl_render,"createPostFilter"): + mari.app.log("OCIODisplay: Error: This version of Mari does not support the mari.gl_render.createPostFilter API") + else: + if OCIO is not None: + if CREATE_FLOATING: + w = OCIODisplayUI() + w.show() + else: + if WINDOW_NAME in mari.app.tabNames(): + mari.app.removeTab(WINDOW_NAME) + w = OCIODisplayUI() + mari.app.addTab(WINDOW_NAME, w) diff --git a/src/mari/prototype/ociofiletransform.py b/src/mari/prototype/ociofiletransform.py new file mode 100644 index 0000000..318c656 --- /dev/null +++ b/src/mari/prototype/ociofiletransform.py @@ -0,0 +1,93 @@ +""" +This script allows the loading of a specied lut (1d/3d/mtx) in +the Mari Viewer. Requires Mari 1.3v2+. + +This example is not represntative of the final Mari OCIO workflow, merely +an API demonstration. This code is a work in progress, to demonstrate the +integration of OpenColorIO and Mari. The APIs this code relies on are subject +to change at any time, and as such should not be relied on for production use +(yet). + +LINUX testing instructions: + +* Build OCIO +mkdir -p dist_mari +mkdir -p build_mari && cd build_mari +cmake -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_INSTALL_PREFIX=../dist_mari \ + -D PYTHON=/usr/bin/python2.6 \ + -D OCIO_NAMESPACE=OpenColorIO_Mari \ + ../ +make install -j8 + +* Edit this file to point to use viewer lut you want to use + +* Run Mari with OpenColorIO added to the LD_LIBRARY_PATH, and Python +env LD_LIBRARY_PATH=<YOURDIR>/dist_mari/lib/ PYTHONPATH=<YOURDIR>/dist_mari/lib/python2.6 mari + +* Source this script in the python console. +Also - IMPORTANT - you must enable 'Use Color Correction' in the Color Manager. + +""" + +# YOU MUST CHANGE THIS TO MODIFY WHICH LUT TO USE +LUT_FILENAME = "/shots/spi/home/lib/lut/dev/v29/luts/LC3DL_Kodak2383_Sony_GDM.3dl" +LUT3D_SIZE = 32 + +import mari, time, os + +try: + import PyOpenColorIO as OCIO + print OCIO.__file__ +except Exception,e: + print "Error: Could not import OpenColorIO python bindings." + print "Please confirm PYTHONPATH has dir containing PyOpenColorIO.so" + +def RegisterOCIOLut(): + if not hasattr(mari.gl_render,"createPostFilter"): + print "Error: This version of Mari does not support the mari.gl_render.createPostFilter API" + return + + config = OCIO.Config() + transform = OCIO.FileTransform(src = os.path.realpath(LUT_FILENAME), + interpolation = OCIO.Constants.INTERP_LINEAR, + direction = OCIO.Constants.TRANSFORM_DIR_FORWARD) + processor = config.getProcessor(transform) + + shaderDesc = dict( [('language', OCIO.Constants.GPU_LANGUAGE_GLSL_1_3), + ('functionName', 'display_ocio_$ID_'), + ('lut3DEdgeLen', LUT3D_SIZE)] ) + shaderText = processor.getGpuShaderText(shaderDesc) + + lut3d = processor.getGpuLut3D(shaderDesc) + + # Clear the existing color managment filter stack + mari.gl_render.clearPostFilterStack() + + # Create variable pre-declarations + desc = "sampler3D lut3d_ocio_$ID_;\n" + desc += shaderText + + # Set the body of the filter + body = "{ Out = display_ocio_$ID_(Out, lut3d_ocio_$ID_); }" + + # HACK: Increment a counter by 1 each time to force a refresh + if not hasattr(mari, "ocio_lut_counter_hack"): + mari.ocio_lut_counter_hack = 0 + else: + mari.ocio_lut_counter_hack += 1 + ocio_lut_counter_hack = mari.ocio_lut_counter_hack + + # Create a new filter + name = "OCIO %s v%d" % (os.path.basename(LUT_FILENAME), ocio_lut_counter_hack) + postfilter = mari.gl_render.createPostFilter(name, desc, body) + + # Set the texture to use for the given sampler on this filter + postfilter.setTexture3D("lut3d_ocio_$ID_", + LUT3D_SIZE, LUT3D_SIZE, LUT3D_SIZE, + postfilter.FORMAT_RGB, lut3d) + + # Append the filter to the end of the current list of filters + mari.gl_render.appendPostFilter(postfilter) + +RegisterOCIOLut() |