From e1cc593687375ef164fc51d330648634a23bb480 Mon Sep 17 00:00:00 2001 From: David Cattermole Date: Sat, 1 Jun 2024 18:10:52 +1000 Subject: [PATCH] Add shader display adjustments to MM ImagePlane. Adds new features and replaces the old Maya-shader shader node approach. The shader node was removed from the image plane. This change is not backwards compatible with previous mmSolver releases :( New display shader features: - Color Gain as RGB colour, not just float. - Exposure adjustment. - Gamma adjustment. - Saturation adjustment. - Soft-clip adjustment. - Saturation Image Channel mode. GitHub issue #254. --- .../AEmmImagePlaneShapeTemplate.mel | 72 +-- .../tools/createimageplane/_lib/constant.py | 19 +- .../tools/createimageplane/_lib/main.py | 97 ++-- .../createimageplane/_lib/mmimageplane.py | 155 +----- .../tools/createimageplane/constant.py | 22 +- python/mmSolver/utils/imageseq.py | 13 + share/shader/mmImagePlane.ogsfx | 98 +++- .../shape/ImagePlaneGeometryOverride.cpp | 442 +++++++++--------- .../shape/ImagePlaneGeometryOverride.h | 34 +- src/mmSolver/shape/ImagePlaneShapeNode.cpp | 223 +++++---- src/mmSolver/shape/ImagePlaneShapeNode.h | 42 +- 11 files changed, 653 insertions(+), 564 deletions(-) diff --git a/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel b/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel index 075ba7ffe..73dbd9dc4 100644 --- a/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel +++ b/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel @@ -1,5 +1,5 @@ // -// Copyright (C) 2022 David Cattermole. +// Copyright (C) 2022, 2024 David Cattermole. // // This file is part of mmSolver. // @@ -97,11 +97,14 @@ global proc AEmmImagePlaneShape_browser( string $cmd = ""; $cmd = $cmd + "import mmSolver.tools.createimageplane.tool as tool;\n"; $cmd = $cmd + "import mmSolver.tools.createimageplane.lib as lib;\n"; + $cmd = $cmd + "\n"; $cmd = $cmd + "image_seq = tool.prompt_user_for_image_sequence();\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "mm_ip_shp = \"" + $image_plane_shp + "\";\n"; + $cmd = $cmd + "attr_name = \"" + $attr_name + "\";\n"; + $cmd = $cmd + "current_slot_attr = \"" + $slot_attr_name + "\";\n"; + $cmd = $cmd + "\n"; $cmd = $cmd + "if image_seq:\n"; - $cmd = $cmd + " mm_ip_shp = \"" + $image_plane_shp + "\";\n"; - $cmd = $cmd + " attr_name = \"" + $attr_name + "\";\n"; - $cmd = $cmd + " current_slot_attr = \"" + $slot_attr_name + "\";\n"; $cmd = $cmd + " if attr_name == current_slot_attr:\n"; $cmd = $cmd + " lib.set_image_sequence(\n"; $cmd = $cmd + " mm_ip_shp,\n"; @@ -132,16 +135,18 @@ global proc AEmmImagePlaneShape_sequenceSlotChanged( AEmmImagePlaneShape_setSlotValue($image_plane_shp, $slot_attr_value); string $cmd = ""; - $cmd = $cmd + "import os.path;\n"; $cmd = $cmd + "import mmSolver.tools.createimageplane.tool as tool;\n"; $cmd = $cmd + "import mmSolver.tools.createimageplane.lib as lib;\n"; + $cmd = $cmd + "\n"; $cmd = $cmd + "mm_ip_shp = \"" + $image_plane_shp + "\";\n"; $cmd = $cmd + "attr_name = \"" + $attr_name + "\";\n"; $cmd = $cmd + "current_slot_attr = \"" + $slot_attr_name + "\";\n"; + $cmd = $cmd + "\n"; $cmd = $cmd + "image_seq = maya.cmds.getAttr(\n"; $cmd = $cmd + " mm_ip_shp + '.' + current_slot_attr) or ''\n"; - $cmd = $cmd + "if len(image_seq) == 0 or not os.path.isfile(image_seq):\n"; + $cmd = $cmd + "if len(image_seq) == 0:\n"; $cmd = $cmd + " image_seq = lib.get_default_image_path();\n"; + $cmd = $cmd + "\n"; $cmd = $cmd + "lib.set_image_sequence(\n"; $cmd = $cmd + " mm_ip_shp,\n"; $cmd = $cmd + " image_seq,\n"; @@ -233,12 +238,15 @@ global proc AEmmImagePlaneShapeTemplate(string $nodeName) editorTemplate -beginLayout "Display" -collapse 0; editorTemplate -addControl "visibleToCameraOnly"; editorTemplate -addSeparator; - editorTemplate -addControl "exposure"; - editorTemplate -addControl "gamma"; editorTemplate -addControl "colorGain"; + editorTemplate -addControl "colorExposure"; + editorTemplate -addControl "colorGamma"; + editorTemplate -addControl "colorSaturation"; + editorTemplate -addControl "colorSoftClip"; editorTemplate -addControl "alphaGain"; - // editorTemplate -addSeparator; - // editorTemplate -addControl "colorSpace"; // Might not be possible. + editorTemplate -addSeparator; + editorTemplate -addControl "imageIgnoreAlpha"; + editorTemplate -addControl "displayChannel"; editorTemplate -endLayout; editorTemplate -beginLayout "Image Sequence" -collapse 0; @@ -270,6 +278,18 @@ global proc AEmmImagePlaneShapeTemplate(string $nodeName) "AEmmImagePlaneShape_imageSequenceReplace" "imageSequenceAlternate3"; + editorTemplate -addSeparator; + editorTemplate -addControl "imageWidth"; + editorTemplate -addControl "imageHeight"; + editorTemplate -addControl "imagePixelAspect"; + editorTemplate -addSeparator; + editorTemplate -addControl "imageSequenceStartFrame"; + editorTemplate -addControl "imageSequenceEndFrame"; + editorTemplate -addSeparator; + // TODO: Use mmColorIO results to allow users to manually give the + // inputColorSpace. + editorTemplate -addControl "inputColorSpace"; + editorTemplate -addSeparator; // TODO: Add radio button to choose what will connect to the // 'imageSequenceFrame' value? Options are: // - Scene Time (time1) @@ -279,15 +299,8 @@ global proc AEmmImagePlaneShapeTemplate(string $nodeName) editorTemplate -addControl "imageSequenceFirstFrame"; editorTemplate -addControl "imageSequenceFrameOutput"; editorTemplate -addSeparator; - editorTemplate -addControl "imageLoadEnable"; - editorTemplate -addControl "imageUseAlphaChannel"; - editorTemplate -addSeparator; - editorTemplate -addControl "imageWidth"; - editorTemplate -addControl "imageHeight"; - editorTemplate -addControl "imagePixelAspect"; - editorTemplate -addSeparator; - editorTemplate -addControl "imageSequenceStartFrame"; - editorTemplate -addControl "imageSequenceEndFrame"; + editorTemplate -addControl "imageFlip"; + editorTemplate -addControl "imageFlop"; editorTemplate -endLayout; editorTemplate -beginLayout "HUD" -collapse 0; @@ -298,24 +311,31 @@ global proc AEmmImagePlaneShapeTemplate(string $nodeName) // TODO: Add 'hudTextColor' - control the HUD text color. editorTemplate -endLayout; + // editorTemplate -beginLayout "Image Cache" -collapse 1; + // // TODO: Add controls to view and edit the image cache. + // editorTemplate -endLayout; + editorTemplate -beginLayout "Miscellaneous" -collapse 1; editorTemplate -addControl "meshResolution"; editorTemplate -addControl "imageDefaultColor"; // Cannot be textured. + editorTemplate -addControl "shaderIsTransparent"; editorTemplate -endLayout; editorTemplate -beginLayout "Nodes" -collapse 1; - editorTemplate -addControl "shaderNode"; - editorTemplate -addControl "shaderFileNode"; editorTemplate -addControl "geometryNode"; editorTemplate -addControl "cameraNode"; editorTemplate -addControl "imagePlaneShapeNode"; editorTemplate -endLayout; - editorTemplate -suppress "imageSequencePadding"; - editorTemplate -suppress "cameraWidthInch"; - editorTemplate -suppress "cameraHeightInch"; - editorTemplate -suppress "lensHashCurrent"; - editorTemplate -suppress "lensHashPrevious"; + // // Internals that we don't want the user to see. + // editorTemplate -suppress "imageSequencePadding"; + // editorTemplate -suppress "cameraWidthInch"; + // editorTemplate -suppress "cameraHeightInch"; + // editorTemplate -suppress "lensHashCurrent"; + // editorTemplate -suppress "lensHashPrevious"; + // editorTemplate -suppress "imageFilePath"; + // editorTemplate -suppress "inputColorSpace"; + // editorTemplate -suppress "outputColorSpace"; AEmmNodeShapeTemplateCommonEnd($nodeName); } diff --git a/python/mmSolver/tools/createimageplane/_lib/constant.py b/python/mmSolver/tools/createimageplane/_lib/constant.py index c8fa6d3bd..20761606c 100644 --- a/python/mmSolver/tools/createimageplane/_lib/constant.py +++ b/python/mmSolver/tools/createimageplane/_lib/constant.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022 David Cattermole. +# Copyright (C) 2022, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -17,3 +17,20 @@ # DEFAULT_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceMain' +ALT_1_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceAlternate1' +ALT_2_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceAlternate2' +ALT_3_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceAlternate3' + +VALID_INPUT_IMAGE_SEQUENCE_ATTR_NAMES = [ + DEFAULT_IMAGE_SEQUENCE_ATTR_NAME, + ALT_1_IMAGE_SEQUENCE_ATTR_NAME, + ALT_2_IMAGE_SEQUENCE_ATTR_NAME, + ALT_3_IMAGE_SEQUENCE_ATTR_NAME, +] + +SHADER_FILE_PATH_ATTR_NAME = 'imageFilePath' +INPUT_COLOR_SPACE_ATTR_NAME = 'inputColorSpace' +OUTPUT_COLOR_SPACE_ATTR_NAME = 'outputColorSpace' + +SCENE_LINEAR_FILE_EXTENSIONS = ['exr', 'sxr'] +SRGB_FILE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'tif', 'tiff', 'tga', 'iff'] diff --git a/python/mmSolver/tools/createimageplane/_lib/main.py b/python/mmSolver/tools/createimageplane/_lib/main.py index 18a8a555f..8f272dd32 100644 --- a/python/mmSolver/tools/createimageplane/_lib/main.py +++ b/python/mmSolver/tools/createimageplane/_lib/main.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020, 2022 David Cattermole. +# Copyright (C) 2020, 2022, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -25,12 +25,13 @@ import mmSolver.logger import mmSolver.api as mmapi import mmSolver.utils.camera as camera_utils +import mmSolver.utils.constant as const_utils +import mmSolver.utils.imageseq as imageseq_utils import mmSolver.utils.python_compat as pycompat import mmSolver.tools.createimageplane.constant as const import mmSolver.tools.createimageplane._lib.constant as lib_const import mmSolver.tools.createimageplane._lib.utilities as lib_utils -import mmSolver.tools.createimageplane._lib.shader as lib_shader import mmSolver.tools.createimageplane._lib.mmimageplane as lib_mmimageplane import mmSolver.tools.createimageplane._lib.polyplane as lib_polyplane import mmSolver.tools.createimageplane._lib.nativeimageplane as lib_nativeimageplane @@ -56,20 +57,18 @@ def create_image_plane_on_camera(cam, name=None): poly_plane_name, mm_ip_tfm, cam_shp ) - name_shade = name + 'Shader' - shader_network = lib_shader.create_network(name_shade, mm_ip_tfm) - name_img_shp = name + 'Shape' mm_ip_shp = lib_mmimageplane.create_shape_node( - name_img_shp, mm_ip_tfm, cam_shp, poly_plane_network, shader_network - ) - - # Shortcut connections to nodes. - lib_utils.force_connect_attr( - shader_network.file_node + '.message', mm_ip_tfm + '.shaderFileNode' + name_img_shp, + mm_ip_tfm, + cam_shp, + poly_plane_network, ) # Logic to calculate the frame number. + # + # TODO: Move this expression into a Maya node, because expressions + # are buggy and not flexible. frame_expr = const.FRAME_EXPRESSION.format(node=mm_ip_shp) frame_expr = frame_expr.replace('{{', '{') frame_expr = frame_expr.replace('}}', '}') @@ -79,17 +78,6 @@ def create_image_plane_on_camera(cam, name=None): shp_node_attr = mm_ip_shp + '.imageSequenceFrameOutput' maya.cmds.setAttr(shp_node_attr, lock=True) - # Set useFrameExtension temporarily. Setting useFrameExtension to - # False causes frameOffset to be locked (but we need to edit it). - is_seq = maya.cmds.getAttr(shader_network.file_node + '.useFrameExtension') - maya.cmds.setAttr(shader_network.file_node + '.useFrameExtension', True) - - file_node_attr = shader_network.file_node + '.frameExtension' - lib_utils.force_connect_attr(shp_node_attr, file_node_attr) - maya.cmds.setAttr(file_node_attr, lock=True) - - maya.cmds.setAttr(shader_network.file_node + '.useFrameExtension', is_seq) - # Image sequence. image_sequence_path = lib_utils.get_default_image_path() set_image_sequence(mm_ip_tfm, image_sequence_path) @@ -120,12 +108,12 @@ def convert_image_planes_on_camera(cam): lib_nativeimageplane.copy_depth_value(mm_ip_tfm, native_ip_shp) - name_shader = name + 'Shader' - shader_network = lib_shader.create_network(name_shader, mm_ip_tfm) - name_img_shp = name + 'Shape' mm_ip_shp = lib_mmimageplane.create_shape_node( - name_img_shp, mm_ip_tfm, cam_shp, poly_plane_network, shader_network + name_img_shp, + mm_ip_tfm, + cam_shp, + poly_plane_network, ) # Disable/hide the Maya image plane. @@ -138,20 +126,65 @@ def convert_image_planes_on_camera(cam): return ip_node_pairs +def _guess_color_space(file_path): + file_extension = file_path.lower().split('.')[-1] + if file_extension in lib_const.SCENE_LINEAR_FILE_EXTENSIONS: + color_space = maya.cmds.mmColorIO(roleSceneLinear=True) + elif file_extension in lib_const.SRGB_FILE_EXTENSIONS: + color_space = maya.cmds.mmColorIO(roleColorPicking=True) + else: + color_space = maya.cmds.mmColorIO(guessColorSpaceFromFile=file_path) + + if not color_space: + color_space = maya.cmds.mmColorIO(roleData=True) + if not color_space: + color_space = maya.cmds.mmColorIO(roleDefault=True) + + exists = maya.cmds.mmColorIO(colorSpaceExists=color_space) + if exists is False: + color_space = None + + return color_space + + def set_image_sequence(mm_image_plane_node, image_sequence_path, attr_name=None): if attr_name is None: attr_name = lib_const.DEFAULT_IMAGE_SEQUENCE_ATTR_NAME + assert isinstance(attr_name, str) + assert attr_name in lib_const.VALID_INPUT_IMAGE_SEQUENCE_ATTR_NAMES tfm, shp = lib_mmimageplane.get_image_plane_node_pair(mm_image_plane_node) if tfm is None or shp is None: LOG.warn('mmImagePlane transform/shape could not be found.') - file_node = lib_mmimageplane.get_file_node(tfm) - if file_node is None: - LOG.warn('mmImagePlane shader file node is invalid.') - if shp is not None: lib_mmimageplane.set_image_sequence(shp, image_sequence_path, attr_name) - if file_node is not None: - lib_shader.set_file_path(file_node, image_sequence_path) + lib_mmimageplane.set_image_sequence( + shp, image_sequence_path, lib_const.SHADER_FILE_PATH_ATTR_NAME + ) + + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME + ( + file_pattern, + _, + _, + _, + _, + ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) + first_frame_file_seq = file_pattern.replace('\\', '/') + + input_color_space = _guess_color_space(first_frame_file_seq) + output_color_space = maya.cmds.mmColorIO(roleSceneLinear=True) + + maya.cmds.setAttr( + shp + '.' + lib_const.INPUT_COLOR_SPACE_ATTR_NAME, + input_color_space, + type='string', + ) + maya.cmds.setAttr( + shp + '.' + lib_const.OUTPUT_COLOR_SPACE_ATTR_NAME, + output_color_space, + type='string', + ) + return diff --git a/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py b/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py index 93040e4be..121109eeb 100644 --- a/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py +++ b/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020, 2022 David Cattermole. +# Copyright (C) 2020, 2022, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -121,68 +121,6 @@ def _create_transform_attrs(image_plane_tfm): def _create_image_plane_shape_attrs(image_plane_shp): - # Exposure attribute - attr = 'exposure' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='double', - softMinValue=-5.0, - softMaxValue=5.0, - defaultValue=0.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Gamma attribute - attr = 'gamma' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='double', - minValue=0.0, - softMaxValue=3.0, - defaultValue=1.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Color Gain attribute - attr = 'colorGain' - default_value = 1.0 - lib_utils.add_attr_float3_color(image_plane_shp, attr, default_value) - - # Alpha Gain attribute - attr = 'alphaGain' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='double', - minValue=0.0, - softMaxValue=1.0, - defaultValue=1.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Use Image Alpha Channel attribute - attr = 'imageUseAlphaChannel' - maya.cmds.addAttr( - image_plane_shp, longName=attr, attributeType='bool', defaultValue=0 - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Default Image Color attribute, display a dark-red color when an - # image is not found. - attr = 'imageDefaultColor' - default_value = 0.0 - lib_utils.add_attr_float3_color(image_plane_shp, attr, default_value) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr + 'R', 0.3) - maya.cmds.setAttr(node_attr + 'G', 0.0) - maya.cmds.setAttr(node_attr + 'B', 0.0) - # Image Load Enable attribute attr = 'imageLoadEnable' maya.cmds.addAttr( @@ -248,6 +186,8 @@ def _create_image_plane_shape_attrs(image_plane_shp): ) node_attr = image_plane_shp + '.' + attr maya.cmds.setAttr(node_attr, keyable=True) + dst_node_attr = image_plane_shp + '.imageFrameNumber' + lib_utils.force_connect_attr(node_attr, dst_node_attr) # Image Sequence details. maya.cmds.addAttr( @@ -320,7 +260,10 @@ def create_transform_node(name_tfm, cam_tfm, cam_shp): def create_shape_node( - name_img_shp, tfm, cam_shp, poly_plane_node_network, shader_node_network + name_img_shp, + tfm, + cam_shp, + poly_plane_node_network, ): """ Convert mesh to a mmImagePlaneShape. @@ -331,13 +274,6 @@ def create_shape_node( img_plane_poly_shp_original = poly_plane_node_network.mesh_shape_original poly_creator = poly_plane_node_network.plane_creator - shd_node = shader_node_network.shd_node - file_node = shader_node_network.file_node - color_gamma_node = shader_node_network.color_gamma_node - alpha_channel_blend_node = shader_node_network.alpha_channel_blend_node - image_load_invert_boolean_node = shader_node_network.image_load_invert_boolean_node - alpha_channel_reverse_node = shader_node_network.alpha_channel_reverse_node - mmapi.load_plugin() shp = maya.cmds.createNode('mmImagePlaneShape', name=name_img_shp, parent=tfm) @@ -365,45 +301,11 @@ def create_shape_node( # Nodes to drive the image plane shape. lib_utils.force_connect_attr(img_plane_poly_shp + '.outMesh', shp + '.geometryNode') - lib_utils.force_connect_attr(shd_node + '.outColor', shp + '.shaderNode') lib_utils.force_connect_attr(cam_shp + '.message', shp + '.cameraNode') # The image drives the pixel aspect ratio of the image plane. lib_utils.force_connect_attr(shp + '.imagePixelAspect', tfm + '.pixelAspect') - # Use the image alpha channel, or not - lib_utils.force_connect_attr( - shp + '.imageUseAlphaChannel', alpha_channel_blend_node + '.blender' - ) - - # Allow user to load the image, or not. - lib_utils.force_connect_attr( - shp + '.imageLoadEnable', image_load_invert_boolean_node + '.inputX' - ) - - # Color Exposure control. - lib_utils.force_connect_attr(shp + '.exposure', file_node + '.exposure') - - # Color Gamma control. - lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaX') - lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaY') - lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaZ') - - # Control file color multiplier - lib_utils.force_connect_attr(shp + '.colorGain', file_node + '.colorGain') - - # Control the alpha gain when 'imageUseAlphaChannel' is disabled. - lib_utils.force_connect_attr(shp + '.alphaGain', file_node + '.alphaGain') - lib_utils.force_connect_attr( - shp + '.alphaGain', alpha_channel_reverse_node + '.inputX' - ) - lib_utils.force_connect_attr( - shp + '.alphaGain', alpha_channel_reverse_node + '.inputY' - ) - lib_utils.force_connect_attr( - shp + '.alphaGain', alpha_channel_reverse_node + '.inputZ' - ) - # Set the camera size of the image plane shape HUD. lib_utils.force_connect_attr( tfm + '.horizontalFilmAperture', shp + '.cameraWidthInch' @@ -412,11 +314,6 @@ def create_shape_node( tfm + '.verticalFilmAperture', shp + '.cameraHeightInch' ) - # Default color for the image plane, when nothing is loaded. - lib_utils.force_connect_attr( - shp + '.imageDefaultColor', file_node + '.defaultColor' - ) - # Mesh Resolution attr drives the plane sub-divisions. node_attr = shp + '.meshResolution' lib_utils.force_connect_attr(node_attr, poly_creator + '.subdivisionsWidth') @@ -427,7 +324,6 @@ def create_shape_node( maya.cmds.setAttr(img_plane_poly_shp + '.intermediateObject', 1) # Add extra message attributes for finding nodes during callbacks. - maya.cmds.addAttr(shp, longName='shaderFileNode', attributeType='message') maya.cmds.addAttr(shp, longName='imagePlaneShapeNode', attributeType='message') return shp @@ -439,16 +335,15 @@ def set_image_sequence(shp, image_sequence_path, attr_name): format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME ( file_pattern, - start_frame, - end_frame, - pad_num, - is_seq, + _, + _, + _, + _, ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) - first_frame_file_seq = file_pattern + first_frame_file_seq = file_pattern.replace('\\', '/') mmapi.load_plugin() try: - first_frame_file_seq = first_frame_file_seq.replace('\\', '/') image_width_height = maya.cmds.mmReadImage( first_frame_file_seq, query=True, widthHeight=True ) @@ -471,6 +366,15 @@ def set_image_sequence(shp, image_sequence_path, attr_name): maya.cmds.setAttr(shp + '.imageWidth', lock=True) maya.cmds.setAttr(shp + '.imageHeight', lock=True) + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED + ( + file_pattern, + start_frame, + end_frame, + pad_num, + is_seq, + ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) + maya.cmds.setAttr(shp + '.' + attr_name, file_pattern, type='string') if not node_utils.node_is_referenced(shp): @@ -530,20 +434,3 @@ def get_image_plane_node_pair(node): tfm = get_transform_node(node) shp = node return tfm, shp - - -def get_file_node(image_plane_tfm): - file_node = None - conns = ( - maya.cmds.listConnections( - image_plane_tfm + '.shaderFileNode', - destination=False, - source=True, - plugs=False, - type='file', - ) - or [] - ) - if len(conns) > 0: - file_node = conns[0] - return file_node diff --git a/python/mmSolver/tools/createimageplane/constant.py b/python/mmSolver/tools/createimageplane/constant.py index 14982842c..ea0aa28e0 100644 --- a/python/mmSolver/tools/createimageplane/constant.py +++ b/python/mmSolver/tools/createimageplane/constant.py @@ -16,6 +16,8 @@ # along with mmSolver. If not, see . # +# NOTE: '{{' and '}}' is used in place of real '{' and '}' characters, +# to allow Python's 'str.format()' to work. DISPLAY_MODE_EXPRESSION = ''' if ({image_plane_tfm}.displayMode == 0) {{ @@ -27,9 +29,25 @@ }} ''' +# NOTE: '{{' and '}}' is used in place of real '{' and '}' characters, +# to allow Python's 'str.format()' to work. FRAME_EXPRESSION = ''' int $start_frame = {node}.imageSequenceStartFrame; +int $end_frame = {node}.imageSequenceEndFrame; int $first_frame = {node}.imageSequenceFirstFrame; -int $user_frame = {node}.imageSequenceFrame; -{node}.imageSequenceFrameOutput = ($start_frame - $first_frame) + $user_frame; +int $input_frame = {node}.imageSequenceFrame; + +int $result = ($start_frame - $first_frame) + $input_frame; + +// // Clamp to start and end frames. +// if ($result < $start_frame) +// {{ +// $result = $start_frame; +// }} +// else if ($result > $end_frame) +// {{ +// $result = $end_frame; +// }} + +{node}.imageSequenceFrameOutput = $result; ''' diff --git a/python/mmSolver/utils/imageseq.py b/python/mmSolver/utils/imageseq.py index 696d0d79e..290888e4f 100644 --- a/python/mmSolver/utils/imageseq.py +++ b/python/mmSolver/utils/imageseq.py @@ -91,6 +91,19 @@ def _get_image_sequence_start_end_frames(base_dir, file_name, file_extension): def expand_image_sequence_path(image_sequence_path, format_style): + """ + Expand a given image sequence path into tokens. + + The tokens are: + - file_pattern: str, the pattern of the file path. + - start_frame: int, first frame of the image sequence. + - end_frame: int, last frame of the image sequence. + - padding_num: int, number of padding digits for the frame number. + - is_seq: bool, is this a sequence? Otherwise it's a single frame. + + :returns: + Tuple of (file_pattern, start_frame, end_frame, padding_num, is_seq) + """ image_sequence_path = os.path.abspath(image_sequence_path) ( diff --git a/share/shader/mmImagePlane.ogsfx b/share/shader/mmImagePlane.ogsfx index 5c8cca55b..faa3b8b27 100644 --- a/share/shader/mmImagePlane.ogsfx +++ b/share/shader/mmImagePlane.ogsfx @@ -22,20 +22,37 @@ // Global variables provided by Maya. uniform mat4 gWVPXf : WorldViewProjection; -// The solid color uniform, its default value and several extra -// parameters -uniform vec4 gSolidColor : DIFFUSE = {1, 1, 1, 1}; -uniform float gColorGain = 1.0f; -uniform float gAlphaGain = 1.0f; +// Color and alpha modifications. +uniform vec4 gColorGain : DIFFUSE = {1, 1, 1, 1}; +uniform float gColorExposure = 0.0f; uniform float gColorGamma = 1.0f; +uniform float gColorSoftClip = 1.0f; +uniform float gAlphaGain = 1.0f; +uniform float4x4 gColorSaturationMatrix < string UIWidget = "None"; >; + +// Should we use the alpha channel from the image? uniform bool gIgnoreAlpha = false; + +// Flip or flop the image. uniform bool gFlip = false; uniform bool gFlop = false; -uniform bool gShowChannelRed = false; -uniform bool gShowChannelGreen = false; -uniform bool gShowChannelBlue = false; -uniform bool gShowChannelAlpha = false; + +// What type of channels to display to the user. +// +// NOTE: Matches the mmsolver::ImageDisplayChannel enum class. +#define DISPLAY_CHANNEL_RGBA (0) +#define DISPLAY_CHANNEL_RGB (1) +#define DISPLAY_CHANNEL_RED (2) +#define DISPLAY_CHANNEL_GREEN (3) +#define DISPLAY_CHANNEL_BLUE (4) +#define DISPLAY_CHANNEL_ALPHA (5) +#define DISPLAY_CHANNEL_LUMINANCE (6) +uniform int gDisplayChannel = 0; + +// When the image is not found the fallback color should be shown. +uniform bool gImageNotFound = false; +uniform vec4 gFallbackColor : DIFFUSE = {0.3f, 0.0f, 0.0f, 1}; // The main image texture displaying RGBA values to the user. uniform texture2D gImageTexture @@ -90,11 +107,38 @@ GLSLShader VS_mmImagePlane { // Pixel Shader Outputs attribute PIXEL_DATA { - vec4 colorOut : COLOR0; + vec4 color_out : COLOR0; } GLSLShader PS_mmImagePlane_Main { + // Simple Reinhard tone-mapping operator. + // + // blend_value is a blend between no adjustment and the full + // tone-mapping (0.0 to 1.0). + vec3 apply_soft_clip(vec3 in_color, float blend_value) { + vec3 adjusted = in_color / (1.0 + in_color); + return mix(in_color, adjusted, blend_value); + } + + vec4 convert_to_luminance_only(vec4 in_color) { + // Luminance weights + // + // From Mozilla: + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance + float r_weight = 0.2126f; // Luminance Red + float g_weight = 0.7152f; // Luminance Green + float b_weight = 0.0722f; // Luminance Blue + + mat4 matrix; + matrix[0] = vec4(r_weight, g_weight, b_weight, 0.0); + matrix[1] = vec4(r_weight, g_weight, b_weight, 0.0); + matrix[2] = vec4(r_weight, g_weight, b_weight, 0.0); + matrix[3] = vec4(0.0, 0.0, 0.0, 1.0); + + return in_color * matrix; + } + // The OCIO function will be replaced and injected at runtime into // this line. This function is used as a stand-in. vec4 OCIODisplay(vec4 passthrough) { return passthrough; } @@ -105,13 +149,15 @@ GLSLShader PS_mmImagePlane_Main { texture_color.a = 1.0f; } - vec3 gamma = vec3(gColorGamma, gColorGamma, gColorGamma); + vec3 gamma = 1.0f / vec3(gColorGamma, gColorGamma, gColorGamma); + + texture_color.rgb *= gColorGain.rgb; + texture_color.rgb *= pow(2.0, gColorExposure); texture_color.rgb = max(vec3(0.0f, 0.0f, 0.0f), texture_color.rgb); - texture_color.rgb = pow(texture_color.rgb, (1.0f / gamma)); + texture_color.rgb = pow(texture_color.rgb, gamma); + texture_color *= gColorSaturationMatrix; + texture_color.rgb = apply_soft_clip(texture_color.rgb, gColorSoftClip); - texture_color.r *= gColorGain; - texture_color.g *= gColorGain; - texture_color.b *= gColorGain; texture_color.a *= gAlphaGain; if (texture_color.a > 1.0f) { @@ -120,15 +166,19 @@ GLSLShader PS_mmImagePlane_Main { texture_color.a = 0.0f; } - colorOut = gSolidColor * texture_color; - if (gShowChannelRed) { - colorOut = vec4(colorOut.r, colorOut.r, colorOut.r, 1.0f); - } else if (gShowChannelGreen) { - colorOut = vec4(colorOut.g, colorOut.g, colorOut.g, 1.0f); - } else if (gShowChannelBlue) { - colorOut = vec4(colorOut.b, colorOut.b, colorOut.b, 1.0f); - } else if (gShowChannelAlpha) { - colorOut = vec4(colorOut.a, colorOut.a, colorOut.a, 1.0f); + color_out = texture_color; + if (gDisplayChannel == DISPLAY_CHANNEL_RGB) { + color_out = vec4(color_out.rgb, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_RED) { + color_out = vec4(color_out.rrr, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_GREEN) { + color_out = vec4(color_out.ggg, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_BLUE) { + color_out = vec4(color_out.bbb, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_ALPHA) { + color_out = vec4(color_out.aaa, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_LUMINANCE) { + color_out = convert_to_luminance_only(color_out); } } } diff --git a/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp b/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp index 777c79449..59470bcf7 100644 --- a/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp +++ b/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp @@ -74,10 +74,14 @@ ImagePlaneGeometryOverride::ImagePlaneGeometryOverride(const MObject &obj) , m_draw_image_size(false) , m_draw_camera_size(false) , m_geometry_node_type(MFn::kInvalid) - , m_use_color_plug(false) , m_image_display_channel(ImageDisplayChannel::kAll) - , m_color_gain(1.0f) + , m_color_gain(1.0, 1.0, 1.0, 1.0) + , m_color_exposure(0.0f) + , m_color_gamma(1.0f) + , m_color_saturation(1.0f) + , m_color_soft_clip(0.0f) , m_alpha_gain(1.0f) + , m_default_color(0.3, 0.0, 0.0, 1.0) , m_ignore_alpha(false) , m_flip(false) , m_flop(false) @@ -280,12 +284,13 @@ void ImagePlaneGeometryOverride::query_node_attributes( bool &out_visible_to_camera_only, bool &out_is_under_camera, bool &out_draw_hud, bool &out_draw_image_size, MString &out_image_size, bool &out_draw_camera_size, MString &out_camera_size, - bool &out_use_color_plug, ImageDisplayChannel &out_image_display_channel, - float &out_color_gain, float &out_alpha_gain, bool &out_ignore_alpha, + ImageDisplayChannel &out_image_display_channel, MColor &out_color_gain, + float &out_color_exposure, float &out_color_gamma, + float &out_color_saturation, float &out_color_soft_clip, + float &out_alpha_gain, MColor &out_default_color, bool &out_ignore_alpha, bool &out_flip, bool &out_flop, bool &out_is_transparent, mmcore::FrameValue &out_frame, MString &out_file_path, - MString &out_input_color_space_name, MString &out_output_color_space_name, - MPlug &out_color_plug) { + MString &out_input_color_space_name, MString &out_output_color_space_name) { const bool verbose = false; MDagPath objPath; @@ -339,62 +344,68 @@ void ImagePlaneGeometryOverride::query_node_attributes( calculate_node_camera_size_string(objPath, double_precision, out_draw_camera_size, out_camera_size); - // Shader attributes. - { - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_use_color_plug, - out_use_color_plug); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_color_gain, + out_color_gain); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_color_gain, - out_color_gain); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_color_exposure, + out_color_exposure); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_alpha_gain, - out_alpha_gain); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_color_gamma, + out_color_gamma); + CHECK_MSTATUS(status); - short image_display_channel_value = 0; - status = - getNodeAttr(objPath, ImagePlaneShapeNode::m_image_display_channel, - image_display_channel_value); - CHECK_MSTATUS(status); - out_image_display_channel = - static_cast(image_display_channel_value); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_color_saturation, + out_color_saturation); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_ignore_alpha, - out_ignore_alpha); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_color_soft_clip, + out_color_soft_clip); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_flip, out_flip); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_alpha_gain, + out_alpha_gain); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_flop, out_flop); - CHECK_MSTATUS(status); + short image_display_channel_value = 0; + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_display_channel, + image_display_channel_value); + CHECK_MSTATUS(status); + out_image_display_channel = + static_cast(image_display_channel_value); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_is_transparent, - out_is_transparent); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_ignore_alpha, + out_ignore_alpha); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_frame_number, - out_frame); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_flip, out_flip); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_file_path, - out_file_path); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_flop, out_flop); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_input_color_space, - out_input_color_space_name); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_shader_is_transparent, + out_is_transparent); + CHECK_MSTATUS(status); - status = getNodeAttr(objPath, ImagePlaneShapeNode::m_output_color_space, - out_output_color_space_name); - CHECK_MSTATUS(status); + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_frame_number, + out_frame); + CHECK_MSTATUS(status); - status = getNodeAttrPlug(objPath, ImagePlaneShapeNode::m_color, - out_color_plug); - CHECK_MSTATUS(status); - } + status = getNodeAttr(objPath, ImagePlaneShapeNode::m_image_file_path, + out_file_path); + CHECK_MSTATUS(status); + + status = + getNodeAttr(objPath, ImagePlaneShapeNode::m_image_input_color_space, + out_input_color_space_name); + CHECK_MSTATUS(status); + + status = + getNodeAttr(objPath, ImagePlaneShapeNode::m_image_output_color_space, + out_output_color_space_name); + CHECK_MSTATUS(status); // Find the input/output file color spaces. // @@ -422,61 +433,28 @@ void find_geometry_node_path(MObject &node, MString &attr_name, MPlugArray connections; bool ok = getUpstreamNodeFromConnection(node, attr_name, connections); - - if (ok) { - for (uint32_t i = 0; i < connections.length(); ++i) { - MObject node = connections[i].node(); - - if (node.hasFn(MFn::kMesh)) { - MDagPath path; - MDagPath::getAPathTo(node, path); - out_geometry_node_path = path; - out_geometry_node_type = path.apiType(); - MMSOLVER_MAYA_VRB( - "Validated geometry node: " - << " path=" - << out_geometry_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - break; - } else { - MMSOLVER_MAYA_WRN( - "Geometry node is not correct type:" - << " path=" - << out_geometry_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - } - } + if (!ok) { + return; } -} - -void find_shader_node(MObject &node, MString &attr_name, - MObject &out_shader_node, - MFn::Type &out_shader_node_type) { - const auto verbose = false; - MPlugArray connections; - bool ok = getUpstreamNodeFromConnection(node, attr_name, connections); - - if (ok) { - for (uint32_t i = 0; i < connections.length(); ++i) { - MObject node = connections[i].node(); - - MFnDependencyNode mfn_depend_node(node); - if (node.hasFn(MFn::kSurfaceShader) || - node.hasFn(MFn::kHwShaderNode) || - node.hasFn(MFn::kPluginHardwareShader) || - node.hasFn(MFn::kPluginHwShaderNode)) { - out_shader_node = node; - out_shader_node_type = node.apiType(); - MMSOLVER_MAYA_VRB("Validated shader node:" - << " name=" << mfn_depend_node.name().asChar() - << " type=" << node.apiTypeStr()); - break; - } else { - MMSOLVER_MAYA_WRN("Shader node is not correct type: " - << " name=" << mfn_depend_node.name().asChar() - << " type=" << node.apiTypeStr()); - } + for (uint32_t i = 0; i < connections.length(); ++i) { + MObject node = connections[i].node(); + + if (node.hasFn(MFn::kMesh)) { + MDagPath path; + MDagPath::getAPathTo(node, path); + out_geometry_node_path = path; + out_geometry_node_type = path.apiType(); + MMSOLVER_MAYA_VRB("Validated geometry node: " + << " path=" + << out_geometry_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); + break; + } else { + MMSOLVER_MAYA_WRN("Geometry node is not correct type:" + << " path=" + << out_geometry_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); } } } @@ -488,27 +466,28 @@ void find_camera_node_path(MObject &node, MString &attr_name, MPlugArray connections; bool ok = getUpstreamNodeFromConnection(node, attr_name, connections); + if (!ok) { + return; + } - if (ok) { - for (uint32_t i = 0; i < connections.length(); ++i) { - MObject node = connections[i].node(); - - if (node.hasFn(MFn::kCamera)) { - MDagPath path; - MDagPath::getAPathTo(node, path); - out_camera_node_path = path; - out_camera_node_type = path.apiType(); - MMSOLVER_MAYA_VRB( - "Validated camera node: " - << " path=" << out_camera_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - break; - } else { - MMSOLVER_MAYA_WRN( - "Camera node is not correct type:" - << " path=" << out_camera_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - } + for (uint32_t i = 0; i < connections.length(); ++i) { + MObject node = connections[i].node(); + + if (node.hasFn(MFn::kCamera)) { + MDagPath path; + MDagPath::getAPathTo(node, path); + out_camera_node_path = path; + out_camera_node_type = path.apiType(); + MMSOLVER_MAYA_VRB("Validated camera node: " + << " path=" + << out_camera_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); + break; + } else { + MMSOLVER_MAYA_WRN("Camera node is not correct type:" + << " path=" + << out_camera_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); } } } @@ -520,12 +499,6 @@ void ImagePlaneGeometryOverride::updateDG() { m_geometry_node_type); } - if (m_shader_node.isNull()) { - MString attr_name = "shaderNode"; - find_shader_node(m_this_node, attr_name, m_shader_node, - m_shader_node_type); - } - if (!m_camera_node_path.isValid()) { MString attr_name = "cameraNode"; find_camera_node_path(m_this_node, attr_name, m_camera_node_path, @@ -538,11 +511,12 @@ void ImagePlaneGeometryOverride::updateDG() { ImagePlaneGeometryOverride::query_node_attributes( m_this_node, m_camera_node_path, m_visible, m_visible_to_camera_only, m_is_under_camera, m_draw_hud, m_draw_image_size, m_image_size, - m_draw_camera_size, m_camera_size, m_use_color_plug, - m_image_display_channel, m_color_gain, m_alpha_gain, m_ignore_alpha, + m_draw_camera_size, m_camera_size, m_image_display_channel, + m_color_gain, m_color_exposure, m_color_gamma, m_color_saturation, + m_color_soft_clip, m_alpha_gain, m_default_color, m_ignore_alpha, m_flip, m_flop, m_is_transparent, m_frame, m_file_path, - temp_input_color_space_name, temp_output_color_space_name, - m_color_plug); + temp_input_color_space_name, temp_output_color_space_name); + if ((m_input_color_space_name.asChar() != temp_input_color_space_name.asChar()) || (m_input_color_space_name.asChar() != @@ -553,83 +527,104 @@ void ImagePlaneGeometryOverride::updateDG() { } } -MTexture *create_color_bars_texture( - MHWRender::MTextureManager *textureManager) { - MHWRender::MTextureDescription texture_desc; - texture_desc.setToDefault2DTexture(); - texture_desc.fWidth = COLOR_BARS_F32_4X4_PIXEL_WIDTH; - texture_desc.fHeight = COLOR_BARS_F32_4X4_PIXEL_HEIGHT; - texture_desc.fDepth = 1; - texture_desc.fBytesPerSlice = - COLOR_BARS_F32_4X4_PIXEL_COUNT * COLOR_BARS_F32_4X4_PIXEL_BYTE_COUNT; - texture_desc.fBytesPerRow = - COLOR_BARS_F32_4X4_PIXEL_WIDTH * COLOR_BARS_F32_4X4_PIXEL_BYTE_COUNT; - texture_desc.fMipmaps = 1; - texture_desc.fArraySlices = 1; - texture_desc.fTextureType = MHWRender::kImage2D; - texture_desc.fFormat = MHWRender::kR32G32B32A32_FLOAT; - - const bool generate_mip_maps = false; - return textureManager->acquireTexture( - "", texture_desc, &(COLOR_BARS_F32_4X4[0]), generate_mip_maps); -} - -MTexture *create_plug_texture(MHWRender::MTextureManager *textureManager, - MPlug &texture_plug) { - MObject texture_node; - MStatus status = get_connected_node(texture_plug, texture_node); - CHECK_MSTATUS(status); - if (status != MS::kSuccess) { - return nullptr; - } - - if (texture_node.isNull()) { - // For when the plug is just a color value, but doesn't have - // any input texture. - const bool generate_mip_maps = false; - const int width = 2; - const int height = 2; - return textureManager->acquireTexture("", texture_plug, width, height, - generate_mip_maps); - } - - const bool allowBackgroundLoad = false; - return textureManager->acquireTexture(texture_node, allowBackgroundLoad); +inline MFloatMatrix create_saturation_matrix(const float saturation) { + // Luminance weights + // + // From Mozilla: + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance + const float kLuminanceRed = 0.2126f; + const float kLuminanceGreen = 0.7152f; + const float kLuminanceBlue = 0.0722f; + + const float r_weight = kLuminanceRed; + const float g_weight = kLuminanceGreen; + const float b_weight = kLuminanceBlue; + + const float r1 = (1.0 - saturation) * r_weight + saturation; + const float r2 = (1.0 - saturation) * r_weight; + const float r3 = (1.0 - saturation) * r_weight; + + const float g1 = (1.0 - saturation) * g_weight; + const float g2 = (1.0 - saturation) * g_weight + saturation; + const float g3 = (1.0 - saturation) * g_weight; + + const float b1 = (1.0 - saturation) * b_weight; + const float b2 = (1.0 - saturation) * b_weight; + const float b3 = (1.0 - saturation) * b_weight + saturation; + + const float saturation_matrix_values[4][4] = { + // Column 0 + {r1, g1, b1, 0.0}, + // Column 1 + {r2, g2, b2, 0.0}, + // Column 2 + {r3, g3, b3, 0.0}, + // Column 3 + {0.0, 0.0, 0.0, 1.0}, + }; + + return MFloatMatrix(saturation_matrix_values); } void ImagePlaneGeometryOverride::set_shader_instance_parameters( MShaderInstance *shader, MHWRender::MTextureManager *textureManager, - const float color_gain, const float alpha_gain, const bool ignore_alpha, - const bool flip, const bool flop, const bool is_transparent, + const MColor &color_gain, const float color_exposure, + const float color_gamma, const float color_saturation, + const float color_soft_clip, const float alpha_gain, + const MColor &default_color, const bool ignore_alpha, const bool flip, + const bool flop, const bool is_transparent, const ImageDisplayChannel image_display_channel, const mmcore::FrameValue frame, const MString &file_path, const MString &input_color_space_name, const MString &output_color_space_name, MHWRender::MTexture *out_color_texture, - const MHWRender::MSamplerState *out_texture_sampler, - const bool use_color_plug, MPlug &out_color_plug) { + const MHWRender::MSamplerState *out_texture_sampler) { + MStatus status = MStatus::kSuccess; const bool verbose = false; MMSOLVER_MAYA_VRB("mmImagePlaneShape: set_shader_instance_parameters."); - static const float color[] = {1.0f, 1.0f, 1.0f, 1.0f}; - shader->setParameter("gSolidColor", color); - shader->setParameter("gColorGain", color_gain); - shader->setParameter("gAlphaGain", alpha_gain); - shader->setParameter("gFlip", flip); - shader->setParameter("gFlop", flop); - shader->setParameter("gIgnoreAlpha", m_ignore_alpha); - shader->setParameter("gShowChannelRed", - image_display_channel == ImageDisplayChannel::kRed); - shader->setParameter("gShowChannelGreen", - image_display_channel == ImageDisplayChannel::kGreen); - shader->setParameter("gShowChannelBlue", - image_display_channel == ImageDisplayChannel::kBlue); - shader->setParameter("gShowChannelAlpha", - image_display_channel == ImageDisplayChannel::kAlpha); - - shader->setIsTransparent(is_transparent); + const float color[] = {color_gain[0], color_gain[1], color_gain[2], 1.0f}; + status = shader->setParameter("gColorGain", color); + CHECK_MSTATUS(status); + + status = shader->setParameter("gColorExposure", color_exposure); + CHECK_MSTATUS(status); + + status = shader->setParameter("gColorGamma", color_gamma); + CHECK_MSTATUS(status); + + MFloatMatrix saturation_matrix = create_saturation_matrix(color_saturation); + status = shader->setParameter("gColorSaturationMatrix", saturation_matrix); + CHECK_MSTATUS(status); + + status = shader->setParameter("gColorSoftClip", color_soft_clip); + CHECK_MSTATUS(status); + + status = shader->setParameter("gAlphaGain", alpha_gain); + CHECK_MSTATUS(status); + + const float temp_default_color[] = {default_color[0], default_color[1], + default_color[2], 1.0f}; + status = shader->setParameter("gFallbackColor", temp_default_color); + CHECK_MSTATUS(status); + + status = shader->setParameter("gFlip", flip); + CHECK_MSTATUS(status); + + status = shader->setParameter("gFlop", flop); + CHECK_MSTATUS(status); + + status = shader->setParameter("gIgnoreAlpha", m_ignore_alpha); + CHECK_MSTATUS(status); + + status = shader->setParameter("gDisplayChannel", + static_cast(image_display_channel)); + CHECK_MSTATUS(status); + + status = shader->setIsTransparent(is_transparent); MMSOLVER_MAYA_VRB("mmImagePlaneShape: shader->isTransparent()=" << shader->isTransparent()); + CHECK_MSTATUS(status); MMSOLVER_MAYA_VRB("mmImagePlaneShape: file_path=" << file_path.asChar()); @@ -641,35 +636,21 @@ void ImagePlaneGeometryOverride::set_shader_instance_parameters( MMSOLVER_MAYA_VRB("mmImagePlaneShape: expanded_file_path=" << expanded_file_path.asChar()); - MMSOLVER_MAYA_VRB( - "mmImagePlaneShape: start use_color_plug=" << use_color_plug); MMSOLVER_MAYA_VRB( "mmImagePlaneShape: start out_color_texture=" << out_color_texture); if (!out_color_texture) { - if (!use_color_plug) { - MMSOLVER_MAYA_VRB("mmImagePlaneShape: use image read"); - const MImage::MPixelType pixel_type = MImage::MPixelType::kByte; - - // // TODO: using kFloat crashes. - // const MImage::MPixelType pixel_type = MImage::MPixelType::kFloat; + MMSOLVER_MAYA_VRB("mmImagePlaneShape: use image read"); + const MImage::MPixelType pixel_type = MImage::MPixelType::kByte; - const bool do_texture_update = false; - ImageCache &image_cache = ImageCache::getInstance(); - out_color_texture = read_image_file( - textureManager, image_cache, m_temp_image, expanded_file_path, - pixel_type, do_texture_update); + // // TODO: using kFloat crashes. + // const MImage::MPixelType pixel_type = MImage::MPixelType::kFloat; - } else { - if (!out_color_plug.isNull()) { - MMSOLVER_MAYA_VRB("mmImagePlaneShape: use color plug texture"); - out_color_texture = - create_plug_texture(textureManager, out_color_plug); - } else { - MMSOLVER_MAYA_VRB("mmImagePlaneShape: use color bars"); - out_color_texture = create_color_bars_texture(textureManager); - } - } + const bool do_texture_update = false; + ImageCache &image_cache = ImageCache::getInstance(); + out_color_texture = + read_image_file(textureManager, image_cache, m_temp_image, + expanded_file_path, pixel_type, do_texture_update); if (out_color_texture) { MMSOLVER_MAYA_VRB("mmImagePlaneShape: texture->name()=" @@ -731,7 +712,9 @@ void ImagePlaneGeometryOverride::set_shader_instance_parameters( } if (out_texture_sampler) { - shader->setParameter("gImageTextureSampler", *out_texture_sampler); + status = + shader->setParameter("gImageTextureSampler", *out_texture_sampler); + CHECK_MSTATUS(status); } else { MMSOLVER_MAYA_WRN("mmImagePlaneShape: Could not get texture sampler." << " out_texture_sampler=" << out_texture_sampler); @@ -740,7 +723,9 @@ void ImagePlaneGeometryOverride::set_shader_instance_parameters( if (out_color_texture) { MHWRender::MTextureAssignment texture_assignment; texture_assignment.texture = out_color_texture; - shader->setParameter("gImageTexture", texture_assignment); + status = shader->setParameter("gImageTexture", texture_assignment); + CHECK_MSTATUS(status); + out_color_texture = nullptr; } else { MMSOLVER_MAYA_VRB( @@ -834,7 +819,8 @@ void ImagePlaneGeometryOverride::updateRenderItems(const MDagPath &path, shaderManager->getStockShader(MShaderManager::k3dSolidShader); if (shader) { static const float color[] = {1.0f, 0.0f, 0.0f, 1.0f}; - shader->setParameter("solidColor", color); + MStatus status = shader->setParameter("solidColor", color); + CHECK_MSTATUS(status); wireframeItem->setShader(shader); shaderManager->releaseShader(shader); } @@ -921,12 +907,12 @@ void ImagePlaneGeometryOverride::updateRenderItems(const MDagPath &path, } set_shader_instance_parameters( - m_shader, textureManager, m_color_gain, m_alpha_gain, - m_ignore_alpha, m_flip, m_flop, m_is_transparent, - m_image_display_channel, m_frame, m_file_path, + m_shader, textureManager, m_color_gain, m_color_exposure, + m_color_gamma, m_color_saturation, m_color_soft_clip, + m_alpha_gain, m_default_color, m_ignore_alpha, m_flip, m_flop, + m_is_transparent, m_image_display_channel, m_frame, m_file_path, m_input_color_space_name, m_output_color_space_name, - m_color_texture, m_texture_sampler, m_use_color_plug, - m_color_plug); + m_color_texture, m_texture_sampler); shadedItem->setShader(m_shader); } @@ -1048,7 +1034,7 @@ void ImagePlaneGeometryOverride::cleanUp() {} #if MAYA_API_VERSION >= 20190000 bool ImagePlaneGeometryOverride::requiresGeometryUpdate() const { const bool verbose = false; - if (m_geometry_node_path.isValid() && !m_shader_node.isNull()) { + if (m_geometry_node_path.isValid()) { MMSOLVER_MAYA_VRB( "ImagePlaneGeometryOverride::requiresGeometryUpdate: false"); return false; diff --git a/src/mmSolver/shape/ImagePlaneGeometryOverride.h b/src/mmSolver/shape/ImagePlaneGeometryOverride.h index cbaa87130..1c752e116 100644 --- a/src/mmSolver/shape/ImagePlaneGeometryOverride.h +++ b/src/mmSolver/shape/ImagePlaneGeometryOverride.h @@ -113,32 +113,34 @@ class ImagePlaneGeometryOverride : public MPxGeometryOverride { bool &out_visible_to_camera_only, bool &out_is_under_camera, bool &out_draw_hud, bool &out_draw_image_size, MString &out_image_size, bool &out_draw_camera_size, MString &out_camera_size, - bool &out_use_color_plug, - ImageDisplayChannel &out_image_display_channel, float &out_color_gain, - float &out_alpha_gain, bool &out_ignore_alpha, bool &out_flip, - bool &out_flop, bool &out_is_transparent, mmcore::FrameValue &out_frame, + ImageDisplayChannel &out_image_display_channel, MColor &out_color_gain, + float &out_color_exposure, float &out_color_gamma, + float &out_color_saturation, float &out_color_soft_clip, + float &out_alpha_gain, MColor &out_default_color, + bool &out_ignore_alpha, bool &out_flip, bool &out_flop, + bool &out_is_transparent, mmcore::FrameValue &out_frame, MString &out_file_path, MString &out_input_color_space_name, - MString &out_output_color_space_name, MPlug &out_color_plug); + MString &out_output_color_space_name); void set_shader_instance_parameters( MShaderInstance *shader, MHWRender::MTextureManager *textureManager, - const float color_gain, const float alpha_gain, const bool ignore_alpha, - const bool flip, const bool flop, const bool is_transparent, + const MColor &color_gain, const float color_exposure, + const float color_gamma, const float color_saturation, + const float color_soft_clip, const float alpha_gain, + const MColor &default_color, const bool ignore_alpha, const bool flip, + const bool flop, const bool is_transparent, const ImageDisplayChannel image_display_channel, const mmcore::FrameValue frame, const MString &file_path, const MString &input_color_space_name, const MString &output_color_space_name, MHWRender::MTexture *out_color_texture, - const MHWRender::MSamplerState *out_texture_sampler, - const bool use_color_plug, MPlug &out_color_plug); + const MHWRender::MSamplerState *out_texture_sampler); MObject m_this_node; MDagPath m_geometry_node_path; MDagPath m_camera_node_path; MFn::Type m_geometry_node_type; - MFn::Type m_shader_node_type; MFn::Type m_camera_node_type; - MObject m_shader_node; bool m_visible; bool m_visible_to_camera_only; @@ -151,13 +153,16 @@ class ImagePlaneGeometryOverride : public MPxGeometryOverride { MString m_camera_size; MCallbackId m_model_editor_changed_callback_id; - bool m_use_color_plug; - // Shader attributes. MShaderInstance *m_shader; ImageDisplayChannel m_image_display_channel; - float m_color_gain; + MColor m_color_gain; float m_alpha_gain; + float m_color_exposure; + float m_color_gamma; + float m_color_saturation; + float m_color_soft_clip; + MColor m_default_color; bool m_ignore_alpha; bool m_flip; bool m_flop; @@ -166,7 +171,6 @@ class ImagePlaneGeometryOverride : public MPxGeometryOverride { MString m_file_path; MString m_input_color_space_name; MString m_output_color_space_name; - MPlug m_color_plug; // Texture caching MImage m_temp_image; diff --git a/src/mmSolver/shape/ImagePlaneShapeNode.cpp b/src/mmSolver/shape/ImagePlaneShapeNode.cpp index 81d92396d..35a1d4ce9 100644 --- a/src/mmSolver/shape/ImagePlaneShapeNode.cpp +++ b/src/mmSolver/shape/ImagePlaneShapeNode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 David Cattermole. + * Copyright (C) 2022, 2024 David Cattermole. * * This file is part of mmSolver. * @@ -77,23 +77,25 @@ MObject ImagePlaneShapeNode::m_camera_height_inch; MObject ImagePlaneShapeNode::m_lens_hash_current; MObject ImagePlaneShapeNode::m_lens_hash_previous; MObject ImagePlaneShapeNode::m_geometry_node; -MObject ImagePlaneShapeNode::m_shader_node; MObject ImagePlaneShapeNode::m_camera_node; -// Shader Attributes -MObject ImagePlaneShapeNode::m_use_color_plug; +// Image Attributes MObject ImagePlaneShapeNode::m_image_display_channel; -MObject ImagePlaneShapeNode::m_color_gain; -MObject ImagePlaneShapeNode::m_alpha_gain; -MObject ImagePlaneShapeNode::m_ignore_alpha; -MObject ImagePlaneShapeNode::m_flip; -MObject ImagePlaneShapeNode::m_flop; -MObject ImagePlaneShapeNode::m_is_transparent; -MObject ImagePlaneShapeNode::m_frame_number; -MObject ImagePlaneShapeNode::m_file_path; -MObject ImagePlaneShapeNode::m_input_color_space; -MObject ImagePlaneShapeNode::m_output_color_space; -MObject ImagePlaneShapeNode::m_color; +MObject ImagePlaneShapeNode::m_image_color_gain; +MObject ImagePlaneShapeNode::m_image_color_exposure; +MObject ImagePlaneShapeNode::m_image_color_gamma; +MObject ImagePlaneShapeNode::m_image_color_saturation; +MObject ImagePlaneShapeNode::m_image_color_soft_clip; +MObject ImagePlaneShapeNode::m_image_alpha_gain; +MObject ImagePlaneShapeNode::m_image_default_color; +MObject ImagePlaneShapeNode::m_image_ignore_alpha; +MObject ImagePlaneShapeNode::m_image_flip; +MObject ImagePlaneShapeNode::m_image_flop; +MObject ImagePlaneShapeNode::m_image_frame_number; +MObject ImagePlaneShapeNode::m_image_file_path; +MObject ImagePlaneShapeNode::m_image_input_color_space; +MObject ImagePlaneShapeNode::m_image_output_color_space; +MObject ImagePlaneShapeNode::m_shader_is_transparent; ImagePlaneShapeNode::ImagePlaneShapeNode() {} @@ -260,13 +262,6 @@ MStatus ImagePlaneShapeNode::initialize() { CHECK_MSTATUS(msgAttr.setKeyable(false)); CHECK_MSTATUS(addAttribute(m_geometry_node)); - m_shader_node = msgAttr.create("shaderNode", "shdnd", &status); - CHECK_MSTATUS(status); - CHECK_MSTATUS(msgAttr.setStorable(true)); - CHECK_MSTATUS(msgAttr.setConnectable(true)); - CHECK_MSTATUS(msgAttr.setKeyable(false)); - CHECK_MSTATUS(addAttribute(m_shader_node)); - m_camera_node = msgAttr.create("cameraNode", "camnd", &status); CHECK_MSTATUS(status); CHECK_MSTATUS(msgAttr.setStorable(true)); @@ -274,120 +269,176 @@ MStatus ImagePlaneShapeNode::initialize() { CHECK_MSTATUS(msgAttr.setKeyable(false)); CHECK_MSTATUS(addAttribute(m_camera_node)); - m_use_color_plug = nAttr.create("useColorPlug", "useclrplg", - MFnNumericData::kBoolean, true); + m_image_color_gain = nAttr.createColor("colorGain", "colgn"); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setReadable(true)); + CHECK_MSTATUS(nAttr.setWritable(true)); + CHECK_MSTATUS(nAttr.setDefault(1.0f, 1.0f, 1.0f)); + CHECK_MSTATUS(addAttribute(m_image_color_gain)); + + const float exposure_soft_min = -9.0f; + const float exposure_soft_max = +9.0f; + const float exposure_default = 0.0f; + m_image_color_exposure = nAttr.create( + "colorExposure", "colexpsr", MFnNumericData::kFloat, exposure_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setSoftMin(exposure_soft_min)); + CHECK_MSTATUS(nAttr.setSoftMax(exposure_soft_max)); + CHECK_MSTATUS(addAttribute(m_image_color_exposure)); + + const float gamma_min = 0.0f; + const float gamma_soft_max = +2.0f; + const float gamma_default = 1.0f; + m_image_color_gamma = nAttr.create("colorGamma", "colgmma", + MFnNumericData::kFloat, gamma_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(gamma_min)); + CHECK_MSTATUS(nAttr.setSoftMax(gamma_soft_max)); + CHECK_MSTATUS(addAttribute(m_image_color_gamma)); + + const float saturation_min = 0.0f; + const float saturation_soft_max = 2.0f; + const float saturation_default = 1.0f; + m_image_color_saturation = + nAttr.create("colorSaturation", "colstrtn", MFnNumericData::kFloat, + saturation_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(saturation_min)); + CHECK_MSTATUS(nAttr.setSoftMax(saturation_soft_max)); + CHECK_MSTATUS(addAttribute(m_image_color_saturation)); + + const float soft_clip_min = 0.0f; + const float soft_clip_max = 1.0f; + const float soft_clip_default = 0.0f; + m_image_color_soft_clip = + nAttr.create("colorSoftClip", "colsftclp", MFnNumericData::kFloat, + soft_clip_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(soft_clip_min)); + CHECK_MSTATUS(nAttr.setMax(soft_clip_max)); + CHECK_MSTATUS(addAttribute(m_image_color_soft_clip)); + + const double alpha_min = 0.0; + const double alpha_max = 1.0; + const double alpha_default = 1.0; + m_image_alpha_gain = nAttr.create("alphaGain", "alpgn", + MFnNumericData::kDouble, alpha_default); CHECK_MSTATUS(nAttr.setStorable(true)); CHECK_MSTATUS(nAttr.setConnectable(true)); CHECK_MSTATUS(nAttr.setKeyable(true)); - CHECK_MSTATUS(addAttribute(m_use_color_plug)); + CHECK_MSTATUS(nAttr.setMin(alpha_min)); + CHECK_MSTATUS(nAttr.setMax(alpha_max)); + CHECK_MSTATUS(addAttribute(m_image_alpha_gain)); // Which channel of the image should be displayed? - short value_all = static_cast(ImageDisplayChannel::kAll); - short value_red = static_cast(ImageDisplayChannel::kRed); - short value_green = static_cast(ImageDisplayChannel::kGreen); - short value_blue = static_cast(ImageDisplayChannel::kBlue); - short value_alpha = static_cast(ImageDisplayChannel::kAlpha); - m_image_display_channel = eAttr.create("shaderImageDisplayChannel", - "shdimgdspchan", value_all, &status); + const short value_all = static_cast(ImageDisplayChannel::kAll); + const short value_rgb = static_cast(ImageDisplayChannel::kRGB); + const short value_red = static_cast(ImageDisplayChannel::kRed); + const short value_green = static_cast(ImageDisplayChannel::kGreen); + const short value_blue = static_cast(ImageDisplayChannel::kBlue); + const short value_alpha = static_cast(ImageDisplayChannel::kAlpha); + const short value_luminance = + static_cast(ImageDisplayChannel::kLuminance); + m_image_display_channel = + eAttr.create("displayChannel", "dspchan", value_all, &status); CHECK_MSTATUS(status); CHECK_MSTATUS(eAttr.addField("RGBA", value_all)); + CHECK_MSTATUS(eAttr.addField("RGB", value_rgb)); CHECK_MSTATUS(eAttr.addField("Red", value_red)); CHECK_MSTATUS(eAttr.addField("Green", value_green)); CHECK_MSTATUS(eAttr.addField("Blue", value_blue)); CHECK_MSTATUS(eAttr.addField("Alpha", value_alpha)); + CHECK_MSTATUS(eAttr.addField("Luminance", value_luminance)); CHECK_MSTATUS(eAttr.setStorable(true)); CHECK_MSTATUS(eAttr.setKeyable(true)); CHECK_MSTATUS(addAttribute(m_image_display_channel)); - m_color_gain = nAttr.create("shaderColorGain", "shdclgn", - MFnNumericData::kDouble, 1.0); - CHECK_MSTATUS(nAttr.setStorable(true)); - CHECK_MSTATUS(nAttr.setConnectable(true)); - CHECK_MSTATUS(nAttr.setKeyable(false)); - CHECK_MSTATUS(nAttr.setMin(0.0)); - CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Color Gain (Shader)"))); - CHECK_MSTATUS(addAttribute(m_color_gain)); - - m_alpha_gain = nAttr.create("shaderAlphaGain", "shdalpgn", - MFnNumericData::kDouble, 1.0); - CHECK_MSTATUS(nAttr.setStorable(true)); - CHECK_MSTATUS(nAttr.setConnectable(true)); - CHECK_MSTATUS(nAttr.setKeyable(false)); - CHECK_MSTATUS(nAttr.setMin(0.0)); - CHECK_MSTATUS(nAttr.setMax(1.0)); - CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Alpha Gain (Shader)"))); - CHECK_MSTATUS(addAttribute(m_alpha_gain)); - - m_ignore_alpha = nAttr.create("shaderIgnoreAlpha", "shdignalp", - MFnNumericData::kBoolean, false); + m_image_ignore_alpha = nAttr.create("imageIgnoreAlpha", "imgignalp", + MFnNumericData::kBoolean, false); CHECK_MSTATUS(nAttr.setStorable(true)); CHECK_MSTATUS(nAttr.setConnectable(true)); CHECK_MSTATUS(nAttr.setKeyable(true)); - CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Ignore Alpha (Shader)"))); - CHECK_MSTATUS(addAttribute(m_ignore_alpha)); + CHECK_MSTATUS(addAttribute(m_image_ignore_alpha)); - m_flip = - nAttr.create("shaderFlip", "shdflip", MFnNumericData::kBoolean, false); + m_image_flip = + nAttr.create("imageFlip", "imgflip", MFnNumericData::kBoolean, false); CHECK_MSTATUS(nAttr.setStorable(true)); CHECK_MSTATUS(nAttr.setConnectable(true)); CHECK_MSTATUS(nAttr.setKeyable(true)); - CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Flip (Shader)"))); - CHECK_MSTATUS(addAttribute(m_flip)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Image Flip (Vertical)"))); + CHECK_MSTATUS(addAttribute(m_image_flip)); - m_flop = - nAttr.create("shaderFlop", "shdflop", MFnNumericData::kBoolean, false); + m_image_flop = + nAttr.create("imageFlop", "imgflop", MFnNumericData::kBoolean, false); CHECK_MSTATUS(nAttr.setStorable(true)); CHECK_MSTATUS(nAttr.setConnectable(true)); CHECK_MSTATUS(nAttr.setKeyable(true)); - CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Flop (Shader)"))); - CHECK_MSTATUS(addAttribute(m_flop)); + CHECK_MSTATUS( + nAttr.setNiceNameOverride(MString("Image Flop (Horizontal)"))); + CHECK_MSTATUS(addAttribute(m_image_flop)); - m_is_transparent = nAttr.create("shaderIsTransparent", "shdistrnsp", - MFnNumericData::kBoolean, false); + m_shader_is_transparent = nAttr.create("shaderIsTransparent", "shdistrnsp", + MFnNumericData::kBoolean, false); CHECK_MSTATUS(nAttr.setStorable(true)); CHECK_MSTATUS(nAttr.setConnectable(true)); CHECK_MSTATUS(nAttr.setKeyable(true)); CHECK_MSTATUS( - nAttr.setNiceNameOverride(MString("Is Transparent (Shader)"))); - CHECK_MSTATUS(addAttribute(m_is_transparent)); + nAttr.setNiceNameOverride(MString("Shader Is Transparent (Debug)"))); + CHECK_MSTATUS(addAttribute(m_shader_is_transparent)); - m_frame_number = - nAttr.create("frameNumber", "frmnmb", MFnNumericData::kInt, 1); + m_image_default_color = nAttr.createColor("imageDefaultColor", "imgdefcol"); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setReadable(true)); + CHECK_MSTATUS(nAttr.setWritable(true)); + CHECK_MSTATUS(nAttr.setDefault(1.0f, 1.0f, 1.0f)); + CHECK_MSTATUS(addAttribute(m_image_default_color)); + + m_image_frame_number = + nAttr.create("imageFrameNumber", "imgfrmnmb", MFnNumericData::kInt, 1); CHECK_MSTATUS(nAttr.setStorable(true)); CHECK_MSTATUS(nAttr.setKeyable(true)); - CHECK_MSTATUS(addAttribute(m_frame_number)); + CHECK_MSTATUS(addAttribute(m_image_frame_number)); + + // // Pixel Data Type + // m_cache_pixel_data_type = eAttr.create( + // "cachePixelDataType", "cchpxldtyp", + // kDataTypeUnknown); + // CHECK_MSTATUS(eAttr.addField("auto", kDataTypeUnknown)); + // CHECK_MSTATUS(eAttr.addField("uint8", kDataTypeUInt8)); + // CHECK_MSTATUS(eAttr.addField("uint16", kDataTypeUInt16)); + // CHECK_MSTATUS(eAttr.addField("half16", kDataTypeHalf16)); + // CHECK_MSTATUS(eAttr.addField("float32", kDataTypeFloat32)); + // CHECK_MSTATUS(eAttr.setStorable(true)); // Create empty string data to be used as attribute default // (string) value. MFnStringData empty_string_data; MObject empty_string_data_obj = empty_string_data.create(""); - m_file_path = tAttr.create("shaderFilePath", "shdflpth", MFnData::kString, - empty_string_data_obj); + m_image_file_path = tAttr.create("imageFilePath", "imgflpth", + MFnData::kString, empty_string_data_obj); CHECK_MSTATUS(tAttr.setStorable(true)); CHECK_MSTATUS(tAttr.setUsedAsFilename(true)); - CHECK_MSTATUS(addAttribute(m_file_path)); + CHECK_MSTATUS(addAttribute(m_image_file_path)); - m_input_color_space = tAttr.create("inputColorSpace", "incolspc", - MFnData::kString, empty_string_data_obj); + m_image_input_color_space = tAttr.create( + "inputColorSpace", "incolspc", MFnData::kString, empty_string_data_obj); CHECK_MSTATUS(tAttr.setStorable(true)); CHECK_MSTATUS(tAttr.setUsedAsFilename(false)); - CHECK_MSTATUS(addAttribute(m_input_color_space)); + CHECK_MSTATUS(addAttribute(m_image_input_color_space)); - m_output_color_space = + m_image_output_color_space = tAttr.create("outputColorSpace", "outcolspc", MFnData::kString, empty_string_data_obj); CHECK_MSTATUS(tAttr.setStorable(true)); CHECK_MSTATUS(tAttr.setUsedAsFilename(false)); - CHECK_MSTATUS(addAttribute(m_output_color_space)); - - m_color = nAttr.createColor("shaderColor", "shdcl"); - CHECK_MSTATUS(nAttr.setDefault(0.0f, 0.58824f, 0.644f)); - CHECK_MSTATUS(nAttr.setStorable(true)); - CHECK_MSTATUS(nAttr.setConnectable(true)); - CHECK_MSTATUS(nAttr.setKeyable(false)); - CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Color (Shader)"))); - CHECK_MSTATUS(addAttribute(m_color)); + CHECK_MSTATUS(addAttribute(m_image_output_color_space)); return MS::kSuccess; } diff --git a/src/mmSolver/shape/ImagePlaneShapeNode.h b/src/mmSolver/shape/ImagePlaneShapeNode.h index d03b6279d..7f2977cd1 100644 --- a/src/mmSolver/shape/ImagePlaneShapeNode.h +++ b/src/mmSolver/shape/ImagePlaneShapeNode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 David Cattermole. + * Copyright (C) 2022, 2024 David Cattermole. * * This file is part of mmSolver. * @@ -44,7 +44,15 @@ namespace mmsolver { -enum class ImageDisplayChannel { kAll = 0, kRed, kGreen, kBlue, kAlpha }; +enum class ImageDisplayChannel { + kAll = 0, + kRGB, + kRed, + kGreen, + kBlue, + kAlpha, + kLuminance +}; class ImagePlaneShapeNode : public MPxLocatorNode { public: @@ -99,23 +107,25 @@ class ImagePlaneShapeNode : public MPxLocatorNode { static MObject m_lens_hash_current; static MObject m_lens_hash_previous; static MObject m_geometry_node; - static MObject m_shader_node; static MObject m_camera_node; + static MObject m_shader_is_transparent; - // Shader Attributes - static MObject m_use_color_plug; + // Image Attributes static MObject m_image_display_channel; - static MObject m_color_gain; - static MObject m_alpha_gain; - static MObject m_ignore_alpha; - static MObject m_flip; - static MObject m_flop; - static MObject m_is_transparent; - static MObject m_frame_number; - static MObject m_file_path; - static MObject m_input_color_space; - static MObject m_output_color_space; - static MObject m_color; + static MObject m_image_color_gain; + static MObject m_image_color_exposure; + static MObject m_image_color_gamma; + static MObject m_image_color_saturation; + static MObject m_image_color_soft_clip; + static MObject m_image_alpha_gain; + static MObject m_image_default_color; + static MObject m_image_ignore_alpha; + static MObject m_image_flip; + static MObject m_image_flop; + static MObject m_image_file_path; + static MObject m_image_frame_number; + static MObject m_image_input_color_space; + static MObject m_image_output_color_space; }; } // namespace mmsolver