From 4d72ad32f71f39c75655ac42035ccfe215c8086a Mon Sep 17 00:00:00 2001 From: rzulak Date: Wed, 14 Apr 2021 17:13:33 -0700 Subject: [PATCH] [usdImagingGL,usdview] Add support to set the OCIO display, view, and colorspace. --- pxr/usdImaging/usdImagingGL/engine.cpp | 15 ++++- pxr/usdImaging/usdImagingGL/engine.h | 6 +- pxr/usdImaging/usdImagingGL/renderParams.h | 9 +++ .../usdImagingGL/wrapRenderParams.cpp | 5 +- pxr/usdImaging/usdviewq/appController.py | 64 ++++++++++++++++++- pxr/usdImaging/usdviewq/stageView.py | 9 ++- .../usdviewq/viewSettingsDataModel.py | 37 +++++++++++ 7 files changed, 136 insertions(+), 9 deletions(-) diff --git a/pxr/usdImaging/usdImagingGL/engine.cpp b/pxr/usdImaging/usdImagingGL/engine.cpp index f6ff0f6ba6..d595f1ec88 100644 --- a/pxr/usdImaging/usdImagingGL/engine.cpp +++ b/pxr/usdImaging/usdImagingGL/engine.cpp @@ -274,7 +274,8 @@ UsdImagingGLEngine::RenderBatch( _PrepareRender(params); - SetColorCorrectionSettings(params.colorCorrectionMode); + SetColorCorrectionSettings(params.colorCorrectionMode, params.ocioDisplay, + params.ocioView, params.ocioColorSpace, params.ocioLook); // XXX App sets the clear color via 'params' instead of setting up Aovs // that has clearColor in their descriptor. So for now we must pass this @@ -1185,7 +1186,11 @@ UsdImagingGLEngine::RestartRenderer() //---------------------------------------------------------------------------- void UsdImagingGLEngine::SetColorCorrectionSettings( - TfToken const& id) + TfToken const& colorCorrectionMode, + TfToken const& ocioDisplay, + TfToken const& ocioView, + TfToken const& ocioColorSpace, + TfToken const& ocioLook) { if (ARCH_UNLIKELY(_legacyImpl)) { return; @@ -1198,7 +1203,11 @@ UsdImagingGLEngine::SetColorCorrectionSettings( TF_VERIFY(_taskController); HdxColorCorrectionTaskParams hdParams; - hdParams.colorCorrectionMode = id; + hdParams.colorCorrectionMode = colorCorrectionMode; + hdParams.displayOCIO = ocioDisplay.GetString(); + hdParams.viewOCIO = ocioView.GetString(); + hdParams.colorspaceOCIO = ocioColorSpace.GetString(); + hdParams.looksOCIO = ocioLook.GetString(); _taskController->SetColorCorrectionParams(hdParams); } diff --git a/pxr/usdImaging/usdImagingGL/engine.h b/pxr/usdImaging/usdImagingGL/engine.h index 13ebeeaf12..2907c7304e 100644 --- a/pxr/usdImaging/usdImagingGL/engine.h +++ b/pxr/usdImaging/usdImagingGL/engine.h @@ -465,7 +465,11 @@ class UsdImagingGLEngine /// Set \p id to one of the HdxColorCorrectionTokens. USDIMAGINGGL_API void SetColorCorrectionSettings( - TfToken const& id); + TfToken const& ccType, + TfToken const& ocioDisplay = {}, + TfToken const& ocioView = {}, + TfToken const& ocioColorSpace = {}, + TfToken const& ocioLook = {}); /// @} diff --git a/pxr/usdImaging/usdImagingGL/renderParams.h b/pxr/usdImaging/usdImagingGL/renderParams.h index 9cf13b1694..2bc37edf47 100644 --- a/pxr/usdImaging/usdImagingGL/renderParams.h +++ b/pxr/usdImaging/usdImagingGL/renderParams.h @@ -100,7 +100,12 @@ class UsdImagingGLRenderParams bool enableUsdDrawModes; GfVec4f clearColor; TfToken colorCorrectionMode; + // Optional OCIO color setings, only valid when colorCorrectionMode==HdxColorCorrectionTokens->openColorIO int lut3dSizeOCIO; + TfToken ocioDisplay; + TfToken ocioView; + TfToken ocioColorSpace; + TfToken ocioLook; inline UsdImagingGLRenderParams(); @@ -168,6 +173,10 @@ UsdImagingGLRenderParams::operator==(const UsdImagingGLRenderParams &other) && enableUsdDrawModes == other.enableUsdDrawModes && clearColor == other.clearColor && colorCorrectionMode == other.colorCorrectionMode + && ocioDisplay == other.ocioDisplay + && ocioView == other.ocioView + && ocioColorSpace == other.ocioColorSpace + && ocioLook == other.ocioLook && lut3dSizeOCIO == other.lut3dSizeOCIO; } diff --git a/pxr/usdImaging/usdImagingGL/wrapRenderParams.cpp b/pxr/usdImaging/usdImagingGL/wrapRenderParams.cpp index fc690c6d77..bfa0504091 100644 --- a/pxr/usdImaging/usdImagingGL/wrapRenderParams.cpp +++ b/pxr/usdImaging/usdImagingGL/wrapRenderParams.cpp @@ -85,6 +85,9 @@ wrapRenderParams() .def_readwrite("enableUsdDrawModes", &Params::enableUsdDrawModes) .def_readwrite("colorCorrectionMode", &Params::colorCorrectionMode) .def_readwrite("clearColor", &Params::clearColor) - .def_readwrite("lut3dSizeOCIO", &Params::lut3dSizeOCIO) + .def_readwrite("ocioDisplay", &Params::ocioDisplay) + .def_readwrite("ocioView", &Params::ocioView) + .def_readwrite("ocioColorSpace", &Params::ocioColorSpace) + .def_readwrite("ocioLook", &Params::ocioLook) ; } diff --git a/pxr/usdImaging/usdviewq/appController.py b/pxr/usdImaging/usdviewq/appController.py index 4303362cfd..e2e7f2ae6c 100644 --- a/pxr/usdImaging/usdviewq/appController.py +++ b/pxr/usdImaging/usdviewq/appController.py @@ -76,7 +76,7 @@ from .settings2 import StateSource from .usdviewApi import UsdviewApi from .rootDataModel import RootDataModel, ChangeNotice -from .viewSettingsDataModel import ViewSettingsDataModel +from .viewSettingsDataModel import ViewSettingsDataModel, OCIOSettings from . import plugin from .pythonInterpreter import Myconsole @@ -650,6 +650,7 @@ def __init__(self, parserData, resolverContextFn): self._ui.actionOpenColorIO) for action in self._colorCorrectionActions: self._ui.colorCorrectionActionGroup.addAction(action) + self._ocioSettings, self._ocioMenu = None, None # XXX This should be a validator in ViewSettingsDataModel. if self._dataModel.viewSettings.renderMode not in RenderModes: @@ -942,7 +943,7 @@ def __init__(self, parserData, resolverContextFn): self._ui.renderModeActionGroup.triggered.connect(self._changeRenderMode) self._ui.colorCorrectionActionGroup.triggered.connect( - self._changeColorCorrection) + lambda mode: self._changeColorCorrection(str(mode.text()))) self._ui.pickModeActionGroup.triggered.connect(self._changePickMode) @@ -1671,6 +1672,63 @@ def _configureColorManagement(self): UsdImagingGL.Engine.IsColorCorrectionCapable()) self._ui.menuColorCorrection.setEnabled(enableMenu) + try: + import PyOpenColorIO as OCIO + config = OCIO.GetCurrentConfig() + ocioMenu = QtWidgets.QMenu('OpenColorIO') + colorSpaceMenu = QtWidgets.QMenu('ColorSpace') + #looksMenu = QtWidgets.QMenu('Looks') + colorSpaceMap = {} + + def addAction(menu, name): + action = menu.addAction(name) + action.setCheckable(True) + return action + + def setColorSpace(action): + if self._ocioSettings._colorSpace: + self._ocioSettings._colorSpace.setChecked(False) + self._ocioSettings._colorSpace = action + self._changeColorCorrection(ColorCorrectionModes.OPENCOLORIO) + self._refreshColorCorrectionModeMenu() + self._dataModel.viewSettings.setOCIOConfig(action.text()) + + def setOCIO(action): + if self._ocioSettings._view: + self._ocioSettings._view.setChecked(False) + self._ocioSettings._display, self._ocioSettings._view = action.parent(), action + # Reset the colorspace to the display & view default + display = self._ocioSettings._display.title() + view = self._ocioSettings._view.text() + colorSpace = config.getDisplayColorSpaceName(display, view) + self._dataModel.viewSettings.setOCIOConfig(colorSpace, display, view) + # This will handle the UI / menu updates + colorSpaceMap[colorSpace].trigger() + + for d in config.getDisplays(): + displayMenu = QtWidgets.QMenu(d) + for v in config.getViews(d): + a = addAction(displayMenu, v) + a.triggered[bool].connect(lambda _, action=a: setOCIO(action)) + ocioMenu.addMenu(displayMenu) + + for cs in config.getColorSpaces(): + colorSpace = cs.getName() + a = addAction(colorSpaceMenu, colorSpace) + colorSpaceMap[colorSpace] = a + a.triggered[bool].connect(lambda _, action=a: setColorSpace(action)) + + # for lk in config.getLooks(): + # addAction(looksMenu, lk) + + ocioMenu.addMenu(colorSpaceMenu) + #ocioMenu.addMenu(looksMenu) + self._ui.menuColorCorrection.addMenu(ocioMenu) + self._ocioSettings, self._ocioMenu = OCIOSettings(None), ocioMenu + + except ImportError: + return + # Topology-dependent UI changes def _reloadVaryingUI(self): @@ -2369,7 +2427,7 @@ def _changeRenderMode(self, mode): self._dataModel.viewSettings.renderMode = str(mode.text()) def _changeColorCorrection(self, mode): - self._dataModel.viewSettings.colorCorrectionMode = str(mode.text()) + self._dataModel.viewSettings.colorCorrectionMode = mode def _changePickMode(self, mode): self._dataModel.viewSettings.pickMode = str(mode.text()) diff --git a/pxr/usdImaging/usdviewq/stageView.py b/pxr/usdImaging/usdviewq/stageView.py index 01b0d311cc..37aa4dfb3b 100644 --- a/pxr/usdImaging/usdviewq/stageView.py +++ b/pxr/usdImaging/usdviewq/stageView.py @@ -1452,9 +1452,16 @@ def renderSinglePass(self, renderMode, renderSelHighlights): self._renderParams.highlight = renderSelHighlights self._renderParams.enableSceneMaterials = self._dataModel.viewSettings.enableSceneMaterials self._renderParams.enableSceneLights = self._dataModel.viewSettings.enableSceneLights - self._renderParams.colorCorrectionMode = self._dataModel.viewSettings.colorCorrectionMode self._renderParams.clearColor = Gf.Vec4f(self._dataModel.viewSettings.clearColor) + ccMode = self._dataModel.viewSettings.colorCorrectionMode + self._renderParams.colorCorrectionMode = ccMode + self._renderParams.ocioDisplay, self._renderParams.ocioView, self._renderParams.ocioColorSpace = \ + (self._dataModel.viewSettings.ocioConfig.display, + self._dataModel.viewSettings.ocioConfig.view, + self._dataModel.viewSettings.ocioConfig.colorSpace) if ccMode == ColorCorrectionModes.OPENCOLORIO else \ + ('','','') + pseudoRoot = self._dataModel.stage.GetPseudoRoot() renderer.SetSelectionColor(self._dataModel.viewSettings.highlightColor) diff --git a/pxr/usdImaging/usdviewq/viewSettingsDataModel.py b/pxr/usdImaging/usdviewq/viewSettingsDataModel.py index 63d0fd944d..2e79ee70d3 100644 --- a/pxr/usdImaging/usdviewq/viewSettingsDataModel.py +++ b/pxr/usdImaging/usdviewq/viewSettingsDataModel.py @@ -73,6 +73,25 @@ def wrapper(self, *args, **kwargs): self.signalSettingChanged.emit() return wrapper +"""Class to hold OCIO display, view, and colorSpace settings. +The underlying data is somewhat opaque (for view it is strings, but +for an app-controler it may be the Qt object) +""" +class OCIOSettings(): + def __init__(self, dflt=None): + self._display, self._view, self._colorSpace = dflt, dflt, dflt + + @property + def display(self): + return self._display + + @property + def view(self): + return self._view + + @property + def colorSpace(self): + return self._colorSpace class ViewSettingsDataModel(QtCore.QObject, StateSource): """Data model containing settings related to the rendered view of a USD @@ -110,6 +129,7 @@ def __init__(self, rootDataModel, parent): self._renderMode = self.stateProperty("renderMode", default=RenderModes.SMOOTH_SHADED) self._freeCameraFOV = self.stateProperty("freeCameraFOV", default=60.0) self._colorCorrectionMode = self.stateProperty("colorCorrectionMode", default=ColorCorrectionModes.SRGB) + self._ocioSettings = OCIOSettings('') self._pickMode = self.stateProperty("pickMode", default=PickModes.PRIMS) # We need to store the trinary selHighlightMode state here, @@ -311,6 +331,23 @@ def colorCorrectionMode(self): def colorCorrectionMode(self, value): self._colorCorrectionMode = value + @property + def ocioConfig(self): + return self._colorCorrectionMode + + @property + def ocioConfig(self): + return self._ocioSettings + + def setOCIOConfig(self, colorSpace=None, display=None, view=None): + if display: + assert view, 'Cannot set a display without a view' + self._ocioSettings._display = display + self._ocioSettings._view = view + if colorSpace: + self._ocioSettings._colorSpace = colorSpace + self.colorCorrectionMode = ColorCorrectionModes.OPENCOLORIO + @property def pickMode(self): return self._pickMode