├── requirements.txt ├── Jenkins └── build_test.jenkinsfile ├── .gitignore ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitmodules ├── src └── hydrarpr │ ├── render_studio │ ├── __init__.py │ ├── world │ │ ├── nodes.py │ │ ├── __init__.py │ │ └── node_parser.py │ ├── operators.py │ ├── ui.py │ └── resolver.py │ ├── __init__.py │ ├── logging.py │ ├── preferences.py │ ├── engine.py │ ├── ui.py │ └── properties.py ├── CHANGELOG.md ├── README.md ├── patches ├── usd.diff ├── hdrpr.diff └── usd_opengl_errors_fix.diff └── LICENSE.txt /requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2 2 | -------------------------------------------------------------------------------- /Jenkins/build_test.jenkinsfile: -------------------------------------------------------------------------------- 1 | blender_usdhydra_plugin_pipeline() 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /install 2 | /bin* 3 | /src/hydrarpr/configdev.py 4 | 5 | .idea 6 | .vscode 7 | 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### PURPOSE 2 | What this PR is made for. 3 | 4 | ### EFFECT OF CHANGE 5 | Changes made to project from the user's point of view. 6 | 7 | ### TECHNICAL STEPS 8 | Changes made to code. 9 | 10 | ### NOTES FOR REVIEWERS 11 | Notes, if needed. -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/MaterialX"] 2 | path = deps/MaterialX 3 | url = https://github.com/AcademySoftwareFoundation/MaterialX 4 | [submodule "deps/RadeonProRenderUSD"] 5 | path = deps/RadeonProRenderUSD 6 | url = https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD 7 | [submodule "deps/USD"] 8 | path = deps/USD 9 | url = https://github.com/PixarAnimationStudios/USD 10 | [submodule "deps/RenderStudioKit"] 11 | path = deps/RenderStudioKit 12 | url = git@github.com:Radeon-Pro/RenderStudioKit.git 13 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/__init__.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | from pxr import Plug 16 | 17 | 18 | def register(): 19 | Plug.Registry().GetPluginWithName('RenderStudioResolver').Load() 20 | 21 | from . import resolver, ui, operators 22 | 23 | operators.register() 24 | ui.register() 25 | 26 | 27 | def unregister(): 28 | from . import resolver, ui, operators 29 | 30 | ui.unregister() 31 | operators.unregister() 32 | 33 | if resolver.rs_resolver.is_connected: 34 | resolver.rs_resolver.disconnect() 35 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/world/nodes.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | from .node_parser import NodeParser 16 | 17 | 18 | class ShaderNodeOutputWorld(NodeParser): 19 | def __init__(self, world, node, **kwargs): 20 | super().__init__(world, node, None, **kwargs) 21 | 22 | def export(self): 23 | return self.get_input_link('Surface') 24 | 25 | 26 | class ShaderNodeBackground(NodeParser): 27 | def export(self): 28 | color = self.get_input_value('Color').data 29 | strength = self.get_input_scalar('Strength').data 30 | 31 | return self.node_item({ 32 | 'color': color, 33 | 'intensity': strength 34 | }) 35 | 36 | 37 | class ShaderNodeTexEnvironment(NodeParser): 38 | def export(self): 39 | return self.node_item({ 40 | 'image': self.node.image 41 | }) 42 | 43 | 44 | class ShaderNodeTexImage(NodeParser): 45 | def export(self): 46 | return self.node_item({ 47 | 'image': self.node.image 48 | }) 49 | 50 | 51 | class ShaderNodeRGB(NodeParser): 52 | def export(self): 53 | return self.get_output_default() 54 | 55 | 56 | class ShaderNodeValue(NodeParser): 57 | def export(self): 58 | return self.get_output_default() 59 | 60 | 61 | class ShaderNodeInvert(NodeParser): 62 | def export(self): 63 | fac = self.get_input_scalar('Fac') 64 | color = self.get_input_scalar('Color') 65 | 66 | return fac.blend(color, 1.0 - color) 67 | -------------------------------------------------------------------------------- /src/hydrarpr/__init__.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | from pathlib import Path 16 | import sys 17 | import os 18 | import platform 19 | 20 | from pxr import Plug 21 | 22 | from . import engine, properties, ui, preferences 23 | 24 | 25 | bl_info = { 26 | "name": "Hydra render engine: RPR", 27 | "author": "AMD", 28 | "version": (3, 0, 0), 29 | "blender": (4, 0, 0), 30 | "location": "Info header > Render engine menu", 31 | "description": "Radeon™ ProRender delegate for Hydra render engine", 32 | "tracker_url": "", 33 | "doc_url": "", 34 | "community": "", 35 | "downloads": "", 36 | "main_web": "", 37 | "category": "Render" 38 | } 39 | 40 | LIBS_DIR = Path(__file__).parent / "libs" 41 | 42 | 43 | def register(): 44 | if platform.system() == 'Windows': 45 | os.environ['PATH'] = os.environ['PATH'] + os.pathsep + str(LIBS_DIR / "lib") 46 | 47 | sys.path.append(str(LIBS_DIR / "python")) 48 | Plug.Registry().RegisterPlugins(str(LIBS_DIR / "plugin")) 49 | 50 | preferences.register() 51 | engine.register() 52 | properties.register() 53 | ui.register() 54 | 55 | if platform.system() == 'Windows' and preferences.preferences().rs_enable: 56 | from . import render_studio 57 | render_studio.register() 58 | 59 | try: 60 | from . import configdev 61 | # Example of configdev.py file: 62 | # 63 | # from .preferences import preferences 64 | # pref = preferences() 65 | # pref.rs_workspace_url = "" 66 | # pref.rs_file_format = '.usda' 67 | # pref.log_level = 'DEBUG' 68 | 69 | except ImportError: 70 | pass 71 | 72 | 73 | def unregister(): 74 | if platform.system() == 'Windows' and preferences.preferences().rs_enable: 75 | from . import render_studio 76 | render_studio.unregister() 77 | 78 | ui.unregister() 79 | properties.unregister() 80 | engine.unregister() 81 | preferences.unregister() 82 | -------------------------------------------------------------------------------- /src/hydrarpr/logging.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import sys 16 | import logging.handlers 17 | from pathlib import Path 18 | 19 | 20 | FORMAT_STR = "%(asctime)s %(levelname)s %(name)s [%(thread)d]: %(message)s" 21 | DEFAULT_LEVEL = 'INFO' # available levels: 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL' 22 | BACKUPS = 5 23 | 24 | # root logger for the addon 25 | logger = logging.getLogger('hydrarpr') 26 | logger.setLevel(DEFAULT_LEVEL) 27 | 28 | file_handler = logging.handlers.RotatingFileHandler(Path(__file__).parent / 'hydrarpr.log', 29 | mode='w', encoding='utf-8', delay=True, 30 | backupCount=BACKUPS) 31 | file_handler.doRollover() 32 | file_handler.setFormatter(logging.Formatter(FORMAT_STR)) 33 | logger.addHandler(file_handler) 34 | 35 | console_handler = logging.StreamHandler(stream=sys.stdout) 36 | console_handler.setFormatter(logging.Formatter(FORMAT_STR)) 37 | logger.addHandler(console_handler) 38 | 39 | 40 | def msg(args): 41 | return ", ".join(str(arg) for arg in args) 42 | 43 | 44 | class Log: 45 | def __init__(self, tag): 46 | self.logger = logger.getChild(tag) 47 | 48 | def __call__(self, *args): 49 | self.debug(*args) 50 | 51 | def debug(self, *args): 52 | self.logger.debug(msg(args)) 53 | 54 | def info(self, *args): 55 | self.logger.info(msg(args)) 56 | 57 | def warn(self, *args): 58 | self.logger.warning(msg(args)) 59 | 60 | def error(self, *args): 61 | self.logger.error(msg(args)) 62 | 63 | def critical(self, *args): 64 | self.logger.critical(msg(args)) 65 | 66 | def dump_args(self, func): 67 | """This decorator dumps out the arguments passed to a function before calling it""" 68 | arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] 69 | 70 | def echo_func(*args, **kwargs): 71 | self.debug("<{}>: {}{}".format( 72 | func.__name__, 73 | tuple("{}={}".format(name, arg) for name, arg in zip(arg_names, args)), 74 | # args if args else "", 75 | " {}".format(kwargs.items()) if kwargs else "", 76 | )) 77 | return func(*args, **kwargs) 78 | 79 | return echo_func 80 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 1.1 2 | ## Features: 3 | - The Pixar USD version has been updated to 21.11 with many new features, particularly enabling MaterialX in the OpenGL renderer. 4 | - Support for Blender 3.1 and 3.2 has been added. 5 | - Support for animated USD files has been added for final renders. Furthermore, animated USD files can now be exported from Blender. 6 | - Using Pixar’s RenderMan for final and viewport rendering is now supported. 7 | 8 | ## Fixes: 9 | - Support for the Film Transparent Background option for final renders has been added. 10 | - The export process for instanced geometry has been accelerated. 11 | - Materials could become orange if the user selected them in the viewport render — fixed. 12 | - Fixed issues in the MaterialX editor: 13 | - An issue in the UI when linking nodes in the MaterialX editor has been eliminated; 14 | - Links between incompatible node sockets are not allowed anymore; 15 | - An issue with opening the MaterialX editor when a second window is open Has been fixed; 16 | - Errors if the MaterialX editor is open in the World mode or no objects are selected have been corrected. 17 | - An issue which could lead to preview renders looping infinitely because of textures caching has been fixed. 18 | - Wrong texture coordinates were applied with the OpenGL renderer — fixed. 19 | - With the RPR Interactive mode, point lights could appear as cubes — fixed. 20 | - Results when converting a Blender Principled node to Standard Surface have been improved. 21 | - An issue in rendering scenes with empty material node trees has been fixed. 22 | - Support for the Math shader node has been added. 23 | - Errors when rendering the Animal Logic USD Lab scene have been fixed. 24 | - Undo now works correctly with imported USD objects. 25 | - Changing render modes with viewport rendering active now always updates the view. 26 | - The environment light is now updated correctly when viewport rendering is active and an image is removed from the light color. 27 | - Directional light now works correctly with OpenGL renders. 28 | 29 | 30 | # Version 1.0 31 | 32 | Version 1.0 of the Blender USD Hydra add-on has now been officially released. This includes: 33 | 34 | 35 | - Viewport and final rendering via Hydra using the hdStorm OpenGL renderer as well as one of the Radeon ProRender modes: 36 | - RPR Final — final rendering with the utmost physical correctness and image quality; 37 | - RPR Interactive — faster and more interactive viewport rendering using Vulkan ray-tracing. 38 | - A nodegraph system for assembling and manipulating USD data. The assembled USD hierarchy is displayed in the Blender scene outline, even though USD data is not necessarily loaded into the Blender memory. This allows the manipulation of USD data that is not fully loaded before the rendering time. 39 | - Support for materials via the MaterialX system. MaterialX nodes can be used to create materials, and native Blender materials can be converted to MaterialX. 40 | - Integration with the new GPUOpen online MaterialX Material Library for loading example materials. 41 | -------------------------------------------------------------------------------- /src/hydrarpr/preferences.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import platform 16 | from pathlib import Path 17 | 18 | import bpy 19 | 20 | from . import logging 21 | 22 | 23 | DEFAULT_RS_DIR = Path("C:/" if platform.system() == 'Windows' else "/var/lib") / "AMD/AMD RenderStudio" 24 | 25 | 26 | class RPR_HYDRA_ADDON_PT_preferences(bpy.types.AddonPreferences): 27 | bl_idname = "hydrarpr" 28 | 29 | def rs_enable_update(self, context): 30 | from . import render_studio 31 | if self.rs_enable: 32 | render_studio.register() 33 | else: 34 | render_studio.unregister() 35 | 36 | def log_level_update(self, context): 37 | logging.logger.setLevel(self.log_level) 38 | 39 | log_level: bpy.props.EnumProperty( 40 | name="Log Level", 41 | description="Level of logging", 42 | items=(('DEBUG', "Debug", "Log level: Debug"), 43 | ('INFO', "Info", "Log level: Info"), 44 | ('WARN', "Warning", "Log level: Warning"), 45 | ('ERROR', "Error", "Log level: Error")), 46 | default=logging.DEFAULT_LEVEL, 47 | update=log_level_update, 48 | ) 49 | rs_enable: bpy.props.BoolProperty( 50 | name="AMD RenderStudio", 51 | description="Enable AMD RenderStudio", 52 | default=False, 53 | update=rs_enable_update, 54 | ) 55 | rs_workspace_dir: bpy.props.StringProperty( 56 | name="Workspace Dir", 57 | description="Set directory which would be synchronized for all connected users", 58 | subtype='DIR_PATH', 59 | default=str(DEFAULT_RS_DIR / "Workspace"), 60 | ) 61 | rs_workspace_url: bpy.props.StringProperty( 62 | name="Workspace Url", 63 | description="Set URL of the remote server", 64 | default="", 65 | ) 66 | rs_file_format: bpy.props.EnumProperty( 67 | name="USD File Format", 68 | description="File format of syncing USD file", 69 | items=(('.usd', "usd", "Either of the usda or usdc"), 70 | ('.usda', "usda", "Human-readable UTF-8 text"), 71 | ('.usdc', "usdc", "Random-access \"Crate\" binary")), 72 | default='.usd', 73 | ) 74 | 75 | def draw(self, context): 76 | layout = self.layout 77 | 78 | layout.prop(self, "log_level") 79 | 80 | if platform.system() != 'Windows': 81 | return 82 | 83 | box = layout.box() 84 | row = box.row(align=True) 85 | row.prop(self, "rs_enable") 86 | if self.rs_enable: 87 | col = box.column(align=True) 88 | col.prop(self, "rs_workspace_url") 89 | # col.prop(self, "rs_workspace_dir") 90 | col.prop(self, "rs_file_format") 91 | 92 | 93 | def preferences(): 94 | return bpy.context.preferences.addons["hydrarpr"].preferences 95 | 96 | 97 | register, unregister = bpy.utils.register_classes_factory((RPR_HYDRA_ADDON_PT_preferences,)) 98 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/operators.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import bpy 16 | 17 | from .resolver import rs_resolver 18 | from .ui import tag_redraw 19 | from ..preferences import preferences 20 | 21 | 22 | class RESOLVER_OP_connect(bpy.types.Operator): 23 | bl_idname = 'render_studio.connect' 24 | bl_label = "Connect" 25 | bl_description = "Connect to AMD RenderStudio" 26 | 27 | @classmethod 28 | def poll(cls, context): 29 | pref = preferences() 30 | return pref.rs_workspace_dir and pref.rs_workspace_url 31 | 32 | def execute(self, context): 33 | rs_resolver.connect() 34 | tag_redraw() 35 | 36 | return {'FINISHED'} 37 | 38 | 39 | class RESOLVER_OP_disconnect(bpy.types.Operator): 40 | bl_idname = 'render_studio.disconnect' 41 | bl_label = "Disconnect" 42 | bl_description = "Disconnect from AMD RenderStudio" 43 | 44 | @classmethod 45 | def poll(cls, context): 46 | return rs_resolver.is_connected 47 | 48 | def execute(self, context): 49 | rs_resolver.disconnect() 50 | tag_redraw() 51 | 52 | return {'FINISHED'} 53 | 54 | 55 | class RESOLVER_OP_sync_scene(bpy.types.Operator): 56 | bl_idname = 'render_studio.sync_scene' 57 | bl_label = "Sync Scene" 58 | bl_description = "Sync scene to AMD RenderStudio" 59 | 60 | @classmethod 61 | def poll(cls, context): 62 | return rs_resolver.is_connected 63 | 64 | def execute(self, context): 65 | rs_resolver.sync_scene() 66 | tag_redraw() 67 | 68 | return {'FINISHED'} 69 | 70 | 71 | class RESOLVER_OP_start_live_sync(bpy.types.Operator): 72 | bl_idname = 'render_studio.start_live_sync' 73 | bl_label = "Start Live Sync" 74 | bl_description = "Start live syncing: scene will be synced on every scene update" 75 | 76 | @classmethod 77 | def poll(cls, context): 78 | return rs_resolver.is_connected 79 | 80 | def execute(self, context): 81 | rs_resolver.start_live_sync() 82 | tag_redraw() 83 | 84 | return {'FINISHED'} 85 | 86 | 87 | class RESOLVER_OP_stop_live_sync(bpy.types.Operator): 88 | bl_idname = 'render_studio.stop_live_sync' 89 | bl_label = "Stop Live Sync" 90 | bl_description = "Stop live syncing scene" 91 | 92 | @classmethod 93 | def poll(cls, context): 94 | return rs_resolver.is_connected 95 | 96 | def execute(self, context): 97 | rs_resolver.stop_live_sync() 98 | tag_redraw() 99 | 100 | return {'FINISHED'} 101 | 102 | 103 | register_classes, unregister_classes = bpy.utils.register_classes_factory([ 104 | RESOLVER_OP_connect, 105 | RESOLVER_OP_disconnect, 106 | RESOLVER_OP_sync_scene, 107 | RESOLVER_OP_start_live_sync, 108 | RESOLVER_OP_stop_live_sync, 109 | ]) 110 | 111 | 112 | def register(): 113 | register_classes() 114 | 115 | 116 | def unregister(): 117 | unregister_classes() 118 | -------------------------------------------------------------------------------- /src/hydrarpr/engine.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import bpy 16 | 17 | 18 | class RPRHydraRenderEngine(bpy.types.HydraRenderEngine): 19 | bl_idname = 'RPRHydraRenderEngine' 20 | bl_label = "Hydra RPR" 21 | bl_info = "Hydra Radeon ProRender delegate" 22 | 23 | bl_use_preview = True 24 | bl_use_materialx = True 25 | 26 | bl_delegate_id = "HdRprPlugin" 27 | 28 | def get_render_settings(self, engine_type): 29 | if engine_type == 'VIEWPORT': 30 | settings = bpy.context.scene.hydra_rpr.viewport 31 | quality = settings.interactive_quality 32 | else: 33 | settings = bpy.context.scene.hydra_rpr.final 34 | quality = settings.quality 35 | 36 | denoise = settings.denoise 37 | 38 | result = { 39 | 'rpr:alpha:enable': settings.enable_alpha, 40 | 'rpr:core:renderQuality': settings.render_quality, 41 | 'rpr:core:renderMode': settings.render_mode, 42 | 'rpr:ambientOcclusion:radius': settings.ao_radius, 43 | 'rpr:maxSamples': settings.max_samples, 44 | 'rpr:adaptiveSampling:minSamples': settings.min_adaptive_samples, 45 | 'rpr:adaptiveSampling:noiseTreshold': settings.variance_threshold, 46 | 47 | 'rpr:denoising:enable': denoise.enable, 48 | 'rpr:denoising:minIter': denoise.min_iter, 49 | 'rpr:denoising:iterStep': denoise.iter_step, 50 | } 51 | 52 | if engine_type == 'VIEWPORT': 53 | result |= { 54 | 'rpr:quality:interactive:rayDepth': quality.max_ray_depth, 55 | 'rpr:quality:interactive:downscale:enable': quality.enable_downscale, 56 | 'rpr:quality:interactive:downscale:resolution': quality.resolution_downscale, 57 | } 58 | else: 59 | result |= { 60 | 'rpr:quality:rayDepth': quality.max_ray_depth, 61 | 'rpr:quality:rayDepthDiffuse': quality.max_ray_depth_diffuse, 62 | 'rpr:quality:rayDepthGlossy': quality.max_ray_depth_glossy, 63 | 'rpr:quality:rayDepthRefraction': quality.max_ray_depth_refraction, 64 | 'rpr:quality:rayDepthGlossyRefraction': quality.max_ray_depth_glossy_refraction, 65 | 'rpr:quality:rayDepthShadow': quality.max_ray_depth_shadow, 66 | 'rpr:quality:raycastEpsilon': quality.raycast_epsilon, 67 | 'rpr:quality:radianceClamping': quality.radiance_clamping, 68 | 69 | 'aovToken:Combined': "color", 70 | 'aovToken:Depth': "depth", 71 | 'aovToken:Normal': "normal", 72 | 'aovToken:Position': "worldCoordinate", 73 | } 74 | 75 | if settings.render_quality == 'Northstar': 76 | result['rpr:quality:imageFilterRadius'] = settings.quality.pixel_filter_width 77 | 78 | return result 79 | 80 | def update_render_passes(self, scene, render_layer): 81 | if render_layer.use_pass_z: 82 | self.register_pass(scene, render_layer, 'Depth', 1, 'Z', 'VALUE') 83 | if render_layer.use_pass_normal: 84 | self.register_pass(scene, render_layer, 'Normal', 3, 'XYZ', 'VECTOR') 85 | if render_layer.use_pass_position: 86 | self.register_pass(scene, render_layer, 'Position', 4, 'XYZA', 'VECTOR') 87 | 88 | 89 | register, unregister = bpy.utils.register_classes_factory(( 90 | RPRHydraRenderEngine, 91 | )) 92 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/ui.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import bpy 16 | 17 | from .resolver import rs_resolver 18 | from ..preferences import preferences 19 | from ..ui import Panel 20 | 21 | 22 | class RS_RESOLVER_PT_resolver(Panel): 23 | bl_idname = 'RS_RESOLVER_PT_resolver' 24 | bl_label = "AMD RenderStudio" 25 | 26 | def draw(self, context): 27 | layout = self.layout 28 | layout.use_property_split = True 29 | layout.use_property_decorate = False 30 | 31 | settings = context.scene.hydra_rpr.render_studio 32 | pref = preferences() 33 | if rs_resolver.is_connected: 34 | layout.operator("render_studio.disconnect", icon='UNLINKED') 35 | else: 36 | layout.operator("render_studio.connect", icon='LINKED') 37 | 38 | if (not pref.rs_workspace_url or not pref.rs_workspace_dir) and not rs_resolver.is_connected: 39 | col = layout.box().column(align=True) 40 | if not pref.rs_workspace_url: 41 | col.label(text="Workspace Url is required, check Addon Preferences", icon="ERROR") 42 | 43 | # if not pref.rs_workspace_dir: 44 | # col.label(text="Workspace Dir is required, check Addon Preferences", icon="ERROR") 45 | 46 | layout.prop(settings, "channel") 47 | layout.separator() 48 | 49 | col = layout.column(align=True) 50 | if settings.live_sync: 51 | if rs_resolver.is_live_sync: 52 | col.operator("render_studio.stop_live_sync", icon='CANCEL') 53 | else: 54 | col.operator("render_studio.start_live_sync", icon='FILE_REFRESH') 55 | else: 56 | col.operator("render_studio.sync_scene", icon='FILE_REFRESH') 57 | 58 | row = col.row() 59 | row.enabled = rs_resolver.is_connected 60 | row.use_property_split = False 61 | row.prop(settings, "live_sync") 62 | 63 | if rs_resolver.filename: 64 | col = layout.box().column(align=True) 65 | col.label(text="Syncing to:") 66 | col.label(text=f"{settings.channel}/{rs_resolver.filename}") 67 | 68 | 69 | class RS_RESOLVER_PT_usd_settings(Panel): 70 | bl_parent_id = RS_RESOLVER_PT_resolver.bl_idname 71 | bl_label = "USD Settings" 72 | bl_options = {'DEFAULT_CLOSED'} 73 | 74 | def draw(self, context): 75 | layout = self.layout 76 | settings = context.scene.hydra_rpr.render_studio 77 | 78 | layout.use_property_split = True 79 | layout.use_property_decorate = False 80 | layout.enabled = rs_resolver.is_connected 81 | 82 | col = layout.column(align=True) 83 | col.prop(settings, "selected_objects_only") 84 | col.prop(settings, "visible_objects_only") 85 | 86 | col = layout.column(align=True) 87 | col.prop(settings, "export_animation") 88 | col.prop(settings, "export_hair") 89 | col.prop(settings, "export_world") 90 | col.prop(settings, "use_instancing") 91 | 92 | col = layout.column(align=True) 93 | col.prop(settings, "export_uvmaps") 94 | col.prop(settings, "export_normals") 95 | 96 | col = layout.column(align=True) 97 | col.prop(settings, "export_materials") 98 | col1 = col.column(align=True) 99 | col1.enabled = settings.export_materials 100 | col1.prop(settings, "generate_preview_surface") 101 | col1.prop(settings, "export_textures") 102 | col1.prop(settings, "overwrite_textures") 103 | 104 | col = layout.column() 105 | col.prop(settings, "root_prim_path") 106 | col.prop(settings, "evaluation_mode") 107 | 108 | 109 | def tag_redraw(): 110 | for window in bpy.context.window_manager.windows: 111 | for area in window.screen.areas: 112 | if area.type == 'PROPERTIES': 113 | for region in area.regions: 114 | if region.type == 'WINDOW': 115 | region.tag_redraw() 116 | 117 | 118 | register, unregister = bpy.utils.register_classes_factory(( 119 | RS_RESOLVER_PT_resolver, 120 | RS_RESOLVER_PT_usd_settings, 121 | )) 122 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/resolver.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | from pathlib import Path 16 | 17 | import bpy 18 | from pxr import Tf 19 | 20 | from ..preferences import preferences 21 | 22 | from .. import logging 23 | log = logging.Log("rs.resolver") 24 | 25 | 26 | class Resolver: 27 | def __init__(self): 28 | self.is_connected = False 29 | self.filename = "" 30 | self._connection_listener = Tf.Notice.RegisterGlobally( 31 | "RenderStudioNotice::WorkspaceConnectionChanged", self._connection_callback) 32 | 33 | self._is_depsgraph_update = False 34 | 35 | @property 36 | def is_live_sync(self): 37 | return on_depsgraph_update_post in bpy.app.handlers.depsgraph_update_post 38 | 39 | def _connection_callback(self, notice, sender): 40 | from .ui import tag_redraw 41 | 42 | self.is_connected = notice.IsConnected() 43 | log("RenderStudioNotice::WorkspaceConnectionChanged", notice.IsConnected()) 44 | tag_redraw() 45 | 46 | def connect(self): 47 | from rs import RenderStudioKit 48 | 49 | log("Connecting") 50 | pref = preferences() 51 | RenderStudioKit.SetWorkspaceUrl(pref.rs_workspace_url) 52 | # RenderStudioKit.SetWorkspacePath(pref.rs_workspace_dir) 53 | 54 | try: 55 | RenderStudioKit.SharedWorkspaceConnect(RenderStudioKit.Role.Client) 56 | log.info("Connected") 57 | 58 | except RuntimeError: 59 | log.error("Failed connect to remote server", pref.rs_workspace_url) 60 | 61 | def disconnect(self): 62 | from rs import RenderStudioKit 63 | 64 | if self.is_live_sync: 65 | self.stop_live_sync() 66 | 67 | log("Disconnecting") 68 | RenderStudioKit.SharedWorkspaceDisconnect() 69 | self.filename = "" 70 | log.info("Disconnected") 71 | 72 | def start_live_sync(self): 73 | log("Start live sync") 74 | bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update_post) 75 | 76 | def stop_live_sync(self): 77 | log("Stop live sync") 78 | bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update_post) 79 | 80 | def sync_scene(self): 81 | if self._is_depsgraph_update: 82 | return 83 | 84 | from rs import RenderStudioKit 85 | 86 | pref = preferences() 87 | settings = bpy.context.scene.hydra_rpr.render_studio 88 | 89 | self.filename = Path(bpy.data.filepath).stem if bpy.data.filepath else "untitled" 90 | self.filename += pref.rs_file_format 91 | usd_path = Path(RenderStudioKit.GetWorkspacePath()) / settings.channel / self.filename 92 | 93 | log("Syncing scene", usd_path) 94 | self._is_depsgraph_update = True 95 | USDSyncHook.enable() 96 | try: 97 | bpy.ops.wm.usd_export( 98 | filepath=str(usd_path), 99 | selected_objects_only=settings.selected_objects_only, 100 | visible_objects_only=settings.visible_objects_only, 101 | export_animation=settings.export_animation, 102 | export_hair=settings.export_hair, 103 | export_normals=settings.export_normals, 104 | export_materials=settings.export_materials, 105 | use_instancing=settings.use_instancing, 106 | evaluation_mode=settings.evaluation_mode, 107 | generate_preview_surface=settings.generate_preview_surface, 108 | export_textures=settings.export_textures, 109 | overwrite_textures=settings.overwrite_textures, 110 | root_prim_path=settings.root_prim_path, 111 | ) 112 | finally: 113 | USDSyncHook.disable() 114 | self._is_depsgraph_update = False 115 | 116 | 117 | class USDSyncHook(bpy.types.USDHook): 118 | bl_idname = "usd_sync_hook" 119 | bl_label = "USD Sync Hook" 120 | 121 | @staticmethod 122 | def on_export(export_context): 123 | stage = export_context.get_stage() 124 | if not stage: 125 | return False 126 | 127 | from . import world 128 | settings = bpy.context.scene.hydra_rpr.render_studio 129 | try: 130 | log("Exporting World") 131 | if settings.export_world: 132 | world.sync(stage, export_context.get_depsgraph()) 133 | 134 | except Exception as err: 135 | log.error("Can't sync World", err) 136 | return False 137 | 138 | return True 139 | 140 | @classmethod 141 | def enable(cls): 142 | bpy.utils.register_class(cls) 143 | 144 | @classmethod 145 | def disable(cls): 146 | bpy.utils.unregister_class(cls) 147 | 148 | 149 | rs_resolver = Resolver() 150 | 151 | 152 | def on_depsgraph_update_post(scene, depsgraph): 153 | rs_resolver.sync_scene() 154 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/world/__init__.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | from pathlib import Path 16 | import shutil 17 | 18 | import bpy 19 | from pxr import Sdf, UsdLux 20 | 21 | from ...preferences import preferences 22 | 23 | from ... import logging 24 | 25 | log = logging.Log('export.world') 26 | 27 | SUPPORTED_FORMATS = {".png", ".jpeg", ".jpg", ".hdr", ".tga", ".bmp"} 28 | DEFAULT_FORMAT = ".hdr" 29 | BLENDER_DEFAULT_FORMAT = "HDR" 30 | BLENDER_DEFAULT_COLOR_MODE = "RGB" 31 | READONLY_IMAGE_FORMATS = {".dds"} # blender can read these formats, but can't write 32 | 33 | 34 | def get_world_data(world: bpy.types.World): 35 | data = {'color': (0.05, 0.05, 0.05), 36 | 'image': None, 37 | 'intensity': 1.0, 38 | 'transparency': 1.0} 39 | 40 | if not world: 41 | return data 42 | 43 | if not world.use_nodes: 44 | data['color'] = tuple(world.color) 45 | return data 46 | 47 | output_node = next((node for node in world.node_tree.nodes 48 | if node.bl_idname == 'ShaderNodeOutputWorld' and node.is_active_output), None) 49 | if not output_node: 50 | return data 51 | 52 | from .nodes import ShaderNodeOutputWorld 53 | 54 | node_parser = ShaderNodeOutputWorld(world, output_node) 55 | node_item = node_parser.export() 56 | if not node_item: 57 | return data 58 | 59 | node_data = node_item.data 60 | 61 | if isinstance(node_data, float): 62 | data['color'] = (node_data, node_data, node_data) 63 | return data 64 | 65 | if isinstance(node_data, tuple): 66 | data['color'] = node_data[:3] 67 | data['transparency '] = node_data[3] 68 | return data 69 | 70 | intensity = node_data.get('intensity', 1.0) 71 | if isinstance(intensity, tuple): 72 | intensity = intensity[0] 73 | 74 | data['intensity'] = intensity 75 | 76 | color = node_data.get('color') 77 | if color is None: 78 | image = node_data.get('image') 79 | if image: 80 | data['image'] = cache_image_file(image) 81 | 82 | elif isinstance(color, float): 83 | data['color'] = (color, color, color) 84 | data['transparency'] = color 85 | 86 | elif isinstance(color, tuple): 87 | data['color'] = color[:3] 88 | data['transparency'] = color[3] 89 | 90 | else: # dict 91 | image = color.get('image') 92 | if image: 93 | data['image'] = cache_image_file(image) 94 | 95 | return data 96 | 97 | 98 | def sync(stage, depsgraph): 99 | world = depsgraph.scene.world 100 | if not world: 101 | log.warn("Scene doesn't contain World, nothing to export") 102 | return 103 | 104 | data = get_world_data(world) 105 | 106 | obj_prim = stage.DefinePrim(stage.GetPseudoRoot().GetPath().AppendChild("World")) 107 | usd_light = UsdLux.DomeLight.Define(stage, obj_prim.GetPath().AppendChild("World")) 108 | usd_light.OrientToStageUpAxis() 109 | usd_light.CreateColorAttr(data['color']) 110 | usd_light.CreateIntensityAttr(data['intensity']) 111 | usd_light.GetPrim().CreateAttribute("inputs:transparency", Sdf.ValueTypeNames.Float).Set(data['transparency']) 112 | 113 | if not data['image']: 114 | return 115 | 116 | tex_attr = usd_light.CreateTextureFileAttr() 117 | tex_attr.ClearDefault() 118 | tex_attr.Set(str(data['image'])) 119 | 120 | # set correct Dome light rotation 121 | usd_light.AddRotateYOp().Set(-90.0) 122 | 123 | 124 | def cache_image_file(image: bpy.types.Image): 125 | root_dir = Path(preferences().rs_workspace_dir) / bpy.context.scene.hydra_rpr.render_studio.channel 126 | world_dir = root_dir / "textures/world" 127 | image_path = Path(image.filepath_from_user()) 128 | 129 | if not image.packed_file and image.source != 'GENERATED': 130 | if not image_path.is_file(): 131 | log.warn("Image is missing", image, image_path) 132 | 133 | return None 134 | 135 | image_suffix = image_path.suffix.lower() 136 | 137 | if image_suffix in READONLY_IMAGE_FORMATS or ( 138 | image_suffix in SUPPORTED_FORMATS and 139 | f".{image.file_format.lower()}" in SUPPORTED_FORMATS and not image.is_dirty): 140 | filepath = world_dir / image_path.name 141 | shutil.copy(image_path, filepath) 142 | return filepath.relative_to(root_dir) 143 | 144 | filename = image_path.stem if image_path.stem else image.name 145 | filename += DEFAULT_FORMAT 146 | filepath = world_dir / filename 147 | 148 | scene = bpy.context.scene 149 | user_format = scene.render.image_settings.file_format 150 | user_color_mode = scene.render.image_settings.color_mode 151 | 152 | if not user_color_mode: 153 | user_color_mode = BLENDER_DEFAULT_COLOR_MODE 154 | 155 | scene.render.image_settings.file_format = BLENDER_DEFAULT_FORMAT 156 | scene.render.image_settings.color_mode = BLENDER_DEFAULT_COLOR_MODE 157 | 158 | try: 159 | image.save_render(filepath=str(filepath)) 160 | 161 | except Exception as err: 162 | log.warn("Image isn't exported'", image, filepath, err) 163 | return None 164 | 165 | finally: 166 | scene.render.image_settings.file_format = user_format 167 | scene.render.image_settings.color_mode = user_color_mode 168 | 169 | return filepath.relative_to(root_dir) 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blender USD Hydra Addon 2 | 3 | With Pixar's USD system emerging as a powerful tool for 3D graphics pipelines and interchange. This addon uses AMD Radeon™ ProRender renderer in USD Hydra rendering system in Blender. 4 | 5 | ## Additional Documentation 6 | - [Pixar USD](https://graphics.pixar.com/usd/docs/index.html) 7 | - [Hydra](https://graphics.pixar.com/usd/docs/USD-Glossary.html#USDGlossary-Hydra) 8 | - [MaterialX](http://www.materialx.org/) 9 | 10 | ## Requirements 11 | Currently, this addon works only with [Blender 4.0+](https://www.blender.org/download/) in Windows, Mac OS and Linux. 12 | 13 | On the [releases](https://github.com/GPUOpen-LibrariesAndSDKs/BlenderUSDHydraAddon/releases) page are prebuilt versions of the ready to install addon. 14 | 15 | ## Installing Add-on 16 | 17 | Download the add-on from the releases page [releases](https://github.com/GPUOpen-LibrariesAndSDKs/BlenderUSDHydraAddon/releases). Open Blender preferences and got to the Add-ons section click Install button and pick the add-on in File Browser. Enable the add-on from the Add-ons section. 18 | 19 | ## Usage 20 | ### Rendering 21 | At a simple level, this functions similar to any render addon to Blender, like Cycles or EEVEE which are included in Blender. Simply select the render engine (in this case "Hydra RPR") and render using the `F12` key or starting a viewport render. 22 | 23 | ## Contributing 24 | ### Build Requirements 25 | - Latest Blender precompiled libraries. Clone repository [Blender](https://projects.blender.org/blender/blender) and follow [instructions](https://wiki.blender.org/wiki/Building_Blender#:~:text=for%20Developers.-,Library%20Dependencies,-Details%20on%20obtaining) 26 | - [Python 3.10 x64](https://www.python.org/ftp/python/3.10.11/python-3.10.11.exe) _(Blender 4.0+ uses 3.10)_ 27 | - requirements.txt 28 | 29 | - [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/) _(Windows only)_ 30 | - [CMake 3.22.2+](https://cmake.org/download/). Make sure it's added to the PATH environment variable 31 | - Subversion client, such as [TortoiseSVN](https://tortoisesvn.net/downloads.html) 32 | - [Git for Windows](https://gitforwindows.org/) 33 | 34 | ### Recommended software 35 | - [epydoc](http://epydoc.sourceforge.net/) - enable PyCharm to parse Core's documentation. Use `py -m pip install epydoc` with your selected python interpreter or install it from PyCharm. 36 | - [PyCharm Community Edition](https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC) - recommended for coding, possible to enable intellisense(limited) for Blender code. 37 | - [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads) - has a powerful python extension, possible to enable intellisense for Blender, provides remote debugging in Blender. 38 | 39 | ### Coding Conventions 40 | Aim is to conform to [pep8](https://www.python.org/dev/peps/pep-0008/). 41 | At minimum it's 4 spaces for indentation, sources are utf-8, there's `.gitconfig` in the root of the project - please set you editor to use it (for most simplicity). PyCharm default setting are fine and seems that it also picks up `.editorconfig` automatically also, [Tortoise](https://tortoisegit.org/) Merge has a checkbox 'Enable EditorConfig', for Visual Studio there's [EditorConfig extension](https://visualstudiogallery.msdn.microsoft.com/c8bccfe2-650c-4b42-bc5c-845e21f96328). 42 | 43 | ### Git 44 | We try to avoid merge commits, the easiest way to do it. This one rejects merges that would result in merge commit: 45 | ```commandline 46 | > git config [--global] merge.ff only 47 | ``` 48 | Converts pull to do, essentially, fetch&rebase: 49 | ```commandline 50 | > git config [--global] pull.rebase true 51 | ``` 52 | Also, make more meaningful commits (one commit per feature) the easy way. This will create a single change set from multiple commits coming from ``: 53 | ```commandline 54 | > git merge --squash 55 | ``` 56 | 57 | ### ThirdParty libraries 58 | There is ThirdParty repositories included to the project as a submodules. Please update submodules: 59 | - `deps/MaterialX` https://github.com/AcademySoftwareFoundation/MaterialX 60 | - `deps/RadeonProRenderUSD` https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD 61 | - `deps/USD` https://github.com/PixarAnimationStudios/OpenUSD 62 | 63 | All of them are included via SSH protocol. You will need to create and install [SSH keys](https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh). 64 | 65 | Once SSH keys are installed update/checkout submodules for active branch: 66 | ```commandline 67 | > git submodule update --init -f --recursive 68 | ``` 69 | 70 | ### Build 71 | Require `python 3.10+` to be set by default. 72 | 73 | #### Windows: 74 | Download Blender precompiled libraries. 75 | ```commandline 76 | svn checkout https://svn.blender.org/svnroot/bf-blender/trunk/lib/win64_vc15 77 | ``` 78 | Use cmd.exe or any other command prompt. 79 | ```commandline 80 | > git clone https://github.com/GPUOpen-LibrariesAndSDKs/BlenderUSDHydraAddon 81 | > cd BlenderUSDHydraAddon 82 | > git submodule update --init --recursive 83 | > python build.py -all 84 | ``` 85 | 86 | #### Mac OS: 87 | Download Blender precompiled libraries. 88 | ```commandline 89 | svn checkout https://svn.blender.org/svnroot/bf-blender/trunk/lib/linux_darwin 90 | ``` 91 | ```commandline 92 | > git clone https://github.com/GPUOpen-LibrariesAndSDKs/BlenderUSDHydraAddon 93 | > cd BlenderUSDHydraAddon 94 | > git submodule update --init --recursive 95 | > python tools/build.py -all 96 | ``` 97 | 98 | #### Linux: 99 | Download Blender precompiled libraries. 100 | ```commandline 101 | svn checkout https://svn.blender.org/svnroot/bf-blender/trunk/lib/linux_x86_64_glibc_228 102 | ``` 103 | ```commandline 104 | > git clone https://github.com/GPUOpen-LibrariesAndSDKs/BlenderUSDHydraAddon 105 | > cd BlenderUSDHydraAddon 106 | > git submodule update --init --recursive 107 | > python tools/build.py -all 108 | ``` 109 | 110 | For building on non-default system python version you should change it with `update-alternatives --config python` command or via setting venv. 111 | 112 | #### Build tool 113 | You can build project using `build.py` with different flag combinations. It allows you to create a folder with binaries and pack all the necessary files for development to `/install` folder. Also `build.py` provides a variety of ways to make a project builds: 114 | - `-all` - builds all binaries, equals to `-materialx -usd -hdrpr -addon` 115 | - `-usd` - builds usd binaries 116 | - `-hdrpr` - builds HdRPR plugin binaries 117 | - `-materialx` - builds MaterialX binaries 118 | - `-bin-dir ` - define path to build binaries. _Default_: `bin` 119 | - `-bl-libs-dir ` - define path to Blender precompiled libraries. _Default_: `../lib/` 120 | - `-clean` - removes binaries folder before build, for example: `-all -clean` remove all folders in ``, `-usd -hdrpr -clean` removes only `/Usd` and `/HdRPR` 121 | - `-G ` - set builder, passing with `-all`, `-materialx`, `-usd` and `-hdrpr`. _Example_: `-G "Visual Studio 16 2019"`, `-G "Xcode"` 122 | - `-addon` - generates zip archive with plugin to `./install` folder 123 | 124 | Arguments are mostly used to skip build unneeded binaries. For example: 125 | ```commandline 126 | > python build.py -hdrpr -addon 127 | ``` 128 | ### Debugging 129 | #### Visual Studio 2022 130 | Recommended software for debugging, has really nice mixed python and C stack debugging. Provides to you ability of interactive code evaluation. You can make breakpoints move step by step, watch variables and etc. 131 | 132 | ##### 1. Run Blender with the Add-on 133 | Make sure you have no installed addon for Blender version you want to use; remove installed version if needed. 134 | 135 | ##### 2. Attach Visual Studio to process 136 | Press menu Debug -> Attach to Process... or use hotkey`Ctrl+Alt+P`. In opened window choose Blender process, now you connected and allowed to debug. 137 | Also use build-in Python debugger in realtime. Turn on with `Debug -> Windows -> Python Debug Interactive. 138 | 139 | #### Blender 140 | The easiest way to [build Blender](https://wiki.blender.org/wiki/Building_Blender/Windows) in Release or RelWithDebInfo and add `#pragma optimize( "", off )`. 141 | 142 | #### PyCharm 143 | ```python 144 | import pydevd 145 | pydevd.settrace('localhost', port=52128, stdoutToServer=True, stderrToServer=True, suspend=False) 146 | ``` 147 | -------------------------------------------------------------------------------- /src/hydrarpr/render_studio/world/node_parser.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import math 16 | 17 | import bpy 18 | 19 | from . import log 20 | 21 | 22 | def pass_node_reroute(link): 23 | while isinstance(link.from_node, bpy.types.NodeReroute): 24 | if not link.from_node.inputs[0].links: 25 | return None 26 | 27 | link = link.from_node.inputs[0].links[0] 28 | 29 | return link if link.is_valid else None 30 | 31 | class NodeItem: 32 | """This class is a wrapper used for doing operations on MaterialX nodes, floats, and tuples""" 33 | 34 | def __init__(self, data: [tuple, float, dict]): 35 | self.data = data 36 | 37 | def node_item(self, value): 38 | if isinstance(value, NodeItem): 39 | return value 40 | 41 | return NodeItem(value) 42 | 43 | # MATH OPERATIONS 44 | def _arithmetic_helper(self, other, func): 45 | if other is None: 46 | if isinstance(self.data, float): 47 | result_data = func(self.data) 48 | elif isinstance(self.data, tuple): 49 | result_data = tuple(map(func, self.data)) 50 | else: 51 | result_data = self.data 52 | 53 | else: 54 | other_data = other.data if isinstance(other, NodeItem) else other 55 | if isinstance(self.data, (float, tuple)) and isinstance(other_data, (float, tuple)): 56 | if isinstance(self.data, float) and isinstance(other_data, float): 57 | result_data = func(self.data, other_data) 58 | else: 59 | data = self.data 60 | 61 | # converting data or other_data to have equal length 62 | if isinstance(data, float): 63 | data = (data,) * len(other_data) 64 | elif isinstance(other_data, float): 65 | other_data = (other_data,) * len(data) 66 | elif len(data) < len(other_data): 67 | data = (*data, 1.0) 68 | elif len(other_data) < len(data): 69 | other_data = (*other_data, 1.0) 70 | 71 | result_data = tuple(map(func, data, other_data)) 72 | 73 | else: 74 | result_data = other_data if isinstance(self.data, (float, tuple)) else self.data 75 | 76 | return self.node_item(result_data) 77 | 78 | def __add__(self, other): 79 | return self._arithmetic_helper(other, lambda a, b: a + b) 80 | 81 | def __sub__(self, other): 82 | return self._arithmetic_helper(other, lambda a, b: a - b) 83 | 84 | def __mul__(self, other): 85 | return self._arithmetic_helper(other, lambda a, b: a * b) 86 | 87 | def __truediv__(self, other): 88 | return self._arithmetic_helper(other, lambda a, b: a / b if not math.isclose(b, 0.0) else 0.0) 89 | 90 | def __mod__(self, other): 91 | return self._arithmetic_helper(other, lambda a, b: a % b) 92 | 93 | def __pow__(self, other): 94 | return self._arithmetic_helper(other, lambda a, b: a ** b) 95 | 96 | def __neg__(self): 97 | return 0.0 - self 98 | 99 | def __abs__(self): 100 | return self._arithmetic_helper(None, lambda a: abs(a)) 101 | 102 | def floor(self): 103 | return self._arithmetic_helper(None, lambda a: float(math.floor(a))) 104 | 105 | def ceil(self): 106 | return self._arithmetic_helper(None, lambda a: float(math.ceil(a))) 107 | 108 | # right hand methods for doing something like 1.0 - Node 109 | def __radd__(self, other): 110 | return self + other 111 | 112 | def __rsub__(self, other): 113 | return self.node_item(other) - self 114 | 115 | def __rmul__(self, other): 116 | return self * other 117 | 118 | def __rtruediv__(self, other): 119 | return self.node_item(other) / self 120 | 121 | def __rmod__(self, other): 122 | return self.node_item(other) % self 123 | 124 | def __rpow__(self, other): 125 | return self.node_item(other) ** self 126 | 127 | def dot(self, other): 128 | dot = self._arithmetic_helper(other, lambda a, b: a * b) 129 | if isinstance(dot.data, tuple): 130 | dot.data = sum(dot.data) 131 | 132 | return dot 133 | 134 | def if_else(self, cond: str, other, if_value, else_value): 135 | if cond == '>': 136 | res = self._arithmetic_helper(other, lambda a, b: float(a > b)) 137 | elif cond == '>=': 138 | res = self._arithmetic_helper(other, lambda a, b: float(a >= b)) 139 | elif cond == '==': 140 | res = self._arithmetic_helper(other, lambda a, b: float(a == b)) 141 | elif cond == '<': 142 | return self.node_item(other).if_else('>', self, else_value, if_value) 143 | elif cond == '<=': 144 | return self.node_item(other).if_else('>=', self, else_value, if_value) 145 | elif cond == '!=': 146 | return self.if_else('==', other, else_value, if_value) 147 | else: 148 | raise ValueError("Incorrect condition:", cond) 149 | 150 | if isinstance(res.data, float): 151 | return if_value if res.data == 1.0 else else_value 152 | elif isinstance(res.data, tuple): 153 | return if_value if res.data[0] == 1.0 else else_value 154 | else: 155 | return res 156 | 157 | def min(self, other): 158 | return self._arithmetic_helper(other, lambda a, b: min(a, b)) 159 | 160 | def max(self, other): 161 | return self._arithmetic_helper(other, lambda a, b: max(a, b)) 162 | 163 | def clamp(self, min_val=0.0, max_val=1.0): 164 | """ clamp data to min/max """ 165 | return self.min(max_val).max(min_val) 166 | 167 | def sin(self): 168 | return self._arithmetic_helper(None, lambda a: math.sin(a)) 169 | 170 | def cos(self): 171 | return self._arithmetic_helper(None, lambda a: math.cos(a)) 172 | 173 | def tan(self): 174 | return self._arithmetic_helper(None, lambda a: math.tan(a)) 175 | 176 | def asin(self): 177 | return self._arithmetic_helper(None, lambda a: math.asin(a)) 178 | 179 | def acos(self): 180 | return self._arithmetic_helper(None, lambda a: math.acos(a)) 181 | 182 | def atan(self): 183 | return self._arithmetic_helper(None, lambda a: math.atan(a)) 184 | 185 | def log(self): 186 | return self._arithmetic_helper(None, lambda a: math.log(a)) 187 | 188 | def blend(self, value1, value2): 189 | """ Line interpolate value between value1(0.0) and value2(1.0) by self.data as factor """ 190 | return self * value2 + (1.0 - self) * value1 191 | 192 | 193 | class NodeParser: 194 | """ 195 | This is the base class that parses a blender node. 196 | Subclasses should override only export() function. 197 | """ 198 | 199 | def __init__(self, world: bpy.types.World, 200 | node: bpy.types.Node, out_key, **kwargs): 201 | self.world = world 202 | self.node = node 203 | self.out_key = out_key 204 | self.kwargs = kwargs 205 | 206 | @staticmethod 207 | def get_node_parser_cls(bl_idname): 208 | """ Returns NodeParser class for node_idname or None if not found """ 209 | from . import nodes 210 | return getattr(nodes, bl_idname, None) 211 | 212 | # INTERNAL FUNCTIONS 213 | def _export_node(self, node, out_key, group_node=None): 214 | # getting corresponded NodeParser class 215 | NodeParser_cls = self.get_node_parser_cls(node.bl_idname) 216 | if not NodeParser_cls: 217 | log.warn("Ignoring unsupported node", node, self.world) 218 | return None 219 | 220 | node_parser = NodeParser_cls(self.world, node, out_key, **self.kwargs) 221 | return node_parser.export() 222 | 223 | def _parse_val(self, val): 224 | """Turn blender socket value into python's value""" 225 | 226 | if isinstance(val, (int, float)): 227 | return float(val) 228 | 229 | if len(val) in (3, 4): 230 | return tuple(val) 231 | 232 | if isinstance(val, str): 233 | return val 234 | 235 | raise TypeError("Unknown value type to parse", val) 236 | 237 | def node_item(self, value): 238 | if isinstance(value, NodeItem): 239 | return value 240 | 241 | return NodeItem(value) 242 | 243 | # HELPER FUNCTIONS 244 | # Child classes should use them to do their export 245 | def get_output_default(self): 246 | """ Returns default value of output socket """ 247 | socket_out = self.node.outputs[self.out_key] 248 | 249 | return self.node_item(self._parse_val(socket_out.default_value)) 250 | 251 | def get_input_default(self, in_key): 252 | """ Returns default value of input socket """ 253 | 254 | socket_in = self.node.inputs[in_key] 255 | return self.node_item(self._parse_val(socket_in.default_value)) 256 | 257 | def get_input_link(self, in_key: [str, int]): 258 | """Returns linked parsed node or None if nothing is linked or not link is not valid""" 259 | 260 | socket_in = self.node.inputs[in_key] 261 | if not socket_in.links: 262 | return None 263 | 264 | link = socket_in.links[0] 265 | 266 | # check if linked is correct 267 | if not link.is_valid: 268 | log.warn("Invalid link ignored", link, socket_in, self.node, self.world) 269 | return None 270 | 271 | link = pass_node_reroute(link) 272 | if not link: 273 | return None 274 | 275 | return self._export_node(link.from_node, link.from_socket.identifier) 276 | 277 | def get_input_value(self, in_key): 278 | """ Returns linked node or default socket value """ 279 | 280 | val = self.get_input_link(in_key) 281 | if val is not None: 282 | return val 283 | 284 | return self.get_input_default(in_key) 285 | 286 | def get_input_scalar(self, socket_key): 287 | """ Parse link, accept only RPR core material nodes """ 288 | val = self.get_input_link(socket_key) 289 | if val is not None and isinstance(val.data, (float, tuple)): 290 | return val 291 | 292 | return self.get_input_default(socket_key) 293 | 294 | # EXPORT FUNCTION 295 | def export(self) -> [NodeItem, None]: 296 | """Main export function which should be overridable in child classes""" 297 | return None 298 | -------------------------------------------------------------------------------- /patches/usd.diff: -------------------------------------------------------------------------------- 1 | diff --git a/cmake/defaults/Packages.cmake b/cmake/defaults/Packages.cmake 2 | index 2bb1f30d0..adcc7422e 100644 3 | --- a/cmake/defaults/Packages.cmake 4 | +++ b/cmake/defaults/Packages.cmake 5 | @@ -152,7 +152,7 @@ endif() 6 | 7 | 8 | # --TBB 9 | -find_package(TBB REQUIRED COMPONENTS tbb) 10 | +find_package(TBB) 11 | add_definitions(${TBB_DEFINITIONS}) 12 | 13 | # --math 14 | diff --git a/cmake/defaults/msvcdefaults.cmake b/cmake/defaults/msvcdefaults.cmake 15 | index 1c4cb369d..f5d2064dd 100644 16 | --- a/cmake/defaults/msvcdefaults.cmake 17 | +++ b/cmake/defaults/msvcdefaults.cmake 18 | @@ -124,9 +124,6 @@ _add_define("WIN32_LEAN_AND_MEAN") 19 | # for all translation units. 20 | set(_PXR_CXX_FLAGS "${_PXR_CXX_FLAGS} /bigobj") 21 | 22 | -# Enable PDB generation. 23 | -set(_PXR_CXX_FLAGS "${_PXR_CXX_FLAGS} /Zi") 24 | - 25 | # Enable multiprocessor builds. 26 | set(_PXR_CXX_FLAGS "${_PXR_CXX_FLAGS} /MP") 27 | set(_PXR_CXX_FLAGS "${_PXR_CXX_FLAGS} /Gm-") 28 | diff --git a/cmake/macros/Private.cmake b/cmake/macros/Private.cmake 29 | index 23172745e..8c595b1a1 100644 30 | --- a/cmake/macros/Private.cmake 31 | +++ b/cmake/macros/Private.cmake 32 | @@ -919,12 +919,14 @@ function(_pxr_python_module NAME) 33 | return() 34 | endif() 35 | 36 | - if (WIN32 AND PXR_USE_DEBUG_PYTHON) 37 | + if (WIN32 AND PXR_USE_DEBUG_PYTHON AND NOT CMAKE_DEBUG_POSTFIX) 38 | # On Windows when compiling with debug python the library must be named with _d. 39 | - set(LIBRARY_NAME "_${NAME}_d") 40 | - else() 41 | - set(LIBRARY_NAME "_${NAME}") 42 | - endif() 43 | + # Blender: but this can be skipped if CMAKE_DEBUG_POSTFIX is set, it knows 44 | + # what it is doing and we don't want libraries ending in _d_d.pyd 45 | + set(LIBRARY_NAME "_${NAME}_d") 46 | + else() 47 | + set(LIBRARY_NAME "_${NAME}") 48 | + endif() 49 | 50 | # Install .py files. 51 | if(args_PYTHON_FILES) 52 | diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h 53 | index d78598a84..289ec08d2 100644 54 | --- a/pxr/base/arch/timing.h 55 | +++ b/pxr/base/arch/timing.h 56 | @@ -84,6 +84,10 @@ ArchGetTickTime() 57 | inline uint64_t 58 | ArchGetStartTickTime() 59 | { 60 | + // BLENDER: avoid using rdtsc instruction that is not supported on older CPUs. 61 | + return ArchGetTickTime(); 62 | + 63 | +#if 0 64 | uint64_t t; 65 | #if defined (ARCH_OS_DARWIN) 66 | return ArchGetTickTime(); 67 | @@ -116,6 +120,7 @@ ArchGetStartTickTime() 68 | #error "Unsupported architecture." 69 | #endif 70 | return t; 71 | +#endif 72 | } 73 | 74 | /// Get a "stop" tick time for measuring an interval of time. See 75 | @@ -125,6 +130,10 @@ ArchGetStartTickTime() 76 | inline uint64_t 77 | ArchGetStopTickTime() 78 | { 79 | + // BLENDER: avoid using rdtsc instruction that is not supported on older CPUs. 80 | + return ArchGetTickTime(); 81 | + 82 | +#if 0 83 | uint64_t t; 84 | #if defined (ARCH_OS_DARWIN) 85 | return ArchGetTickTime(); 86 | @@ -155,11 +164,11 @@ ArchGetStopTickTime() 87 | #error "Unsupported architecture." 88 | #endif 89 | return t; 90 | +#endif 91 | } 92 | 93 | -#if defined (doxygen) || \ 94 | - (!defined(ARCH_OS_DARWIN) && defined(ARCH_CPU_INTEL) && \ 95 | - (defined(ARCH_COMPILER_CLANG) || defined(ARCH_COMPILER_GCC))) 96 | +// BLENDER: avoid using rdtsc instruction that is not supported on older CPUs. 97 | +#if 0 98 | 99 | /// A simple timer class for measuring an interval of time using the 100 | /// ArchTickTimer facilities. 101 | diff --git a/pxr/imaging/hdSt/materialXShaderGen.cpp b/pxr/imaging/hdSt/materialXShaderGen.cpp 102 | index be80426a5..a9d60476b 100644 103 | --- a/pxr/imaging/hdSt/materialXShaderGen.cpp 104 | +++ b/pxr/imaging/hdSt/materialXShaderGen.cpp 105 | @@ -137,8 +137,7 @@ HdStMaterialXShaderGen::HdStMaterialXShaderGen( 106 | "st" : mxHdInfo.defaultTexcoordName; 107 | 108 | // Register the customized version of the Surface node generator 109 | - registerImplementation("IM_surface_" + GlslShaderGenerator::TARGET, 110 | - HdStMaterialXSurfaceNodeGen::create); 111 | + registerImplementation("IM_surface_genglsl", HdStMaterialXSurfaceNodeGen::create); 112 | } 113 | 114 | // Based on GlslShaderGenerator::generate() 115 | @@ -275,8 +274,7 @@ HdStMaterialXShaderGen::_EmitMxFunctions( 116 | mx::ShaderStage& mxStage) const 117 | { 118 | // Add global constants and type definitions 119 | - emitLibraryInclude("stdlib/" + mx::GlslShaderGenerator::TARGET 120 | - + "/lib/mx_math.glsl", mxContext, mxStage); 121 | + emitLibraryInclude("stdlib/genglsl/lib/mx_math.glsl", mxContext, mxStage); 122 | emitLine("#if NUM_LIGHTS > 0", mxStage, false); 123 | emitLine("#define MAX_LIGHT_SOURCES NUM_LIGHTS", mxStage, false); 124 | emitLine("#else", mxStage, false); 125 | @@ -396,16 +394,24 @@ HdStMaterialXShaderGen::_EmitMxFunctions( 126 | emitSpecularEnvironment(mxContext, mxStage); 127 | } 128 | if (shadowing) { 129 | - emitLibraryInclude("pbrlib/" + mx::GlslShaderGenerator::TARGET 130 | - + "/lib/mx_shadow.glsl", mxContext, mxStage); 131 | + emitLibraryInclude("pbrlib/genglsl/lib/mx_shadow.glsl", mxContext, mxStage); 132 | } 133 | 134 | +#if MATERIALX_MAJOR_VERSION > 1 || \ 135 | + (MATERIALX_MAJOR_VERSION == 1 && MATERIALX_MINOR_VERSION > 38) || \ 136 | + (MATERIALX_MAJOR_VERSION == 1 && MATERIALX_MINOR_VERSION == 38 && MATERIALX_BUILD_VERSION > 4) 137 | + // MaterialX 1.38.5 changes the default transmission method to "refraction". 138 | + mxContext.getOptions().hwTransmissionRenderMethod = mx::TRANSMISSION_OPACITY; 139 | + 140 | + // Emit transmission code 141 | + emitTransmissionRender(mxContext, mxStage); 142 | +#endif 143 | + 144 | // Emit directional albedo table code. 145 | if (mxContext.getOptions().hwDirectionalAlbedoMethod == 146 | mx::HwDirectionalAlbedoMethod::DIRECTIONAL_ALBEDO_TABLE || 147 | mxContext.getOptions().hwWriteAlbedoTable) { 148 | - emitLibraryInclude("pbrlib/" + mx::GlslShaderGenerator::TARGET 149 | - + "/lib/mx_table.glsl", mxContext, mxStage); 150 | + emitLibraryInclude("pbrlib/genglsl/lib/mx_table.glsl", mxContext, mxStage); 151 | emitLineBreak(mxStage); 152 | } 153 | 154 | @@ -423,7 +429,7 @@ HdStMaterialXShaderGen::_EmitMxFunctions( 155 | // Emit uv transform code globally if needed. 156 | if (mxContext.getOptions().hwAmbientOcclusion) { 157 | emitLibraryInclude( 158 | - "stdlib/" + mx::GlslShaderGenerator::TARGET + "/lib/" + 159 | + "stdlib/genglsl/lib/" + 160 | _tokenSubstitutions[ShaderGenerator::T_FILE_TRANSFORM_UV], 161 | mxContext, mxStage); 162 | } 163 | @@ -492,10 +498,31 @@ HdStMaterialXShaderGen::_EmitMxSurfaceShader( 164 | // closure/shader nodes and need to be emitted first. 165 | emitFunctionCalls(mxGraph, mxContext, mxStage, mx::ShaderNode::Classification::TEXTURE); 166 | 167 | +#if MATERIALX_MAJOR_VERSION == 1 && \ 168 | + MATERIALX_MINOR_VERSION == 38 && \ 169 | + MATERIALX_BUILD_VERSION <= 4 170 | + 171 | // Emit function calls for all surface shader nodes. 172 | // These will internally emit their closure function calls. 173 | emitFunctionCalls(mxGraph, mxContext, mxStage, mx::ShaderNode::Classification::SHADER | 174 | mx::ShaderNode::Classification::SURFACE); 175 | +#else 176 | + // Emit function calls for "root" closure/shader nodes. 177 | + // These will internally emit function calls for any dependent closure nodes upstream. 178 | + for (mx::ShaderGraphOutputSocket* socket : mxGraph.getOutputSockets()) 179 | + { 180 | + if (socket->getConnection()) 181 | + { 182 | + const mx::ShaderNode* upstream = socket->getConnection()->getNode(); 183 | + if (upstream->getParent() == &mxGraph && 184 | + (upstream->hasClassification(mx::ShaderNode::Classification::CLOSURE) || 185 | + upstream->hasClassification(mx::ShaderNode::Classification::SHADER))) 186 | + { 187 | + emitFunctionCall(*upstream, mxContext, mxStage); 188 | + } 189 | + } 190 | + } 191 | +#endif 192 | } 193 | else 194 | { 195 | diff --git a/pxr/imaging/hioOpenVDB/CMakeLists.txt b/pxr/imaging/hioOpenVDB/CMakeLists.txt 196 | index e32762cea..d2c08d3da 100644 197 | --- a/pxr/imaging/hioOpenVDB/CMakeLists.txt 198 | +++ b/pxr/imaging/hioOpenVDB/CMakeLists.txt 199 | @@ -20,6 +20,12 @@ else() 200 | LIST(APPEND __VDB_IMATH_LIBS ${OPENEXR_Half_LIBRARY}) 201 | endif() 202 | 203 | +if (WIN32) 204 | + # OpenVDB uses constants from that aren't available on 205 | + # Windows unless this is defined. 206 | + add_definitions(-D_USE_MATH_DEFINES) 207 | +endif() 208 | + 209 | pxr_library(hioOpenVDB 210 | LIBRARIES 211 | ar 212 | diff --git a/pxr/usd/usdMtlx/reader.cpp b/pxr/usd/usdMtlx/reader.cpp 213 | index 29e901816..e6fc68b20 100644 214 | --- a/pxr/usd/usdMtlx/reader.cpp 215 | +++ b/pxr/usd/usdMtlx/reader.cpp 216 | @@ -797,6 +797,15 @@ _NodeGraphBuilder::_CreateInterfaceInputs( 217 | // We deliberately ignore tokens here. 218 | } 219 | 220 | +mx::StringSet _GetStdlibIncludes() { 221 | + mx::StringSet stdlibIncludes = UsdMtlxGetDocument("")->getReferencedSourceUris(); 222 | + mx::StringSet normStdlibIncludes; 223 | + for (std::string const& entry : stdlibIncludes) { 224 | + normStdlibIncludes.insert(TfNormPath(entry)); 225 | + } 226 | + return normStdlibIncludes; 227 | +} 228 | + 229 | // Returns True if the mtlxNodeDef corresponds to a locally defined custom node 230 | // with an associated nodegraph. 231 | // XXX Locally defined custom nodes without nodegraphs are not supported 232 | @@ -818,13 +827,14 @@ _NodeGraphBuilder::_IsLocalCustomNode(const mx::ConstNodeDefPtr &mtlxNodeDef) 233 | } 234 | // Combine with the nodeDef relative path 235 | nodeDefUri = TfNormPath(fullMtlxPath + nodeDefUri); 236 | + } else { 237 | + nodeDefUri = TfNormPath(nodeDefUri); 238 | } 239 | 240 | // This is a locally defined custom node if the absolute path to the 241 | // nodedef is not included in the stdlibDoc. 242 | static mx::StringSet customNodeDefNames; 243 | - static const mx::StringSet stdlibIncludes = 244 | - UsdMtlxGetDocument("")->getReferencedSourceUris(); 245 | + static const mx::StringSet stdlibIncludes = _GetStdlibIncludes(); 246 | if (stdlibIncludes.find(nodeDefUri) == stdlibIncludes.end()) { 247 | // Check if we already used this custom node 248 | if (std::find(customNodeDefNames.begin(), customNodeDefNames.end(), 249 | diff --git a/pxr/usdImaging/CMakeLists.txt b/pxr/usdImaging/CMakeLists.txt 250 | index d35c59df0..dbf40cc68 100644 251 | --- a/pxr/usdImaging/CMakeLists.txt 252 | +++ b/pxr/usdImaging/CMakeLists.txt 253 | @@ -7,7 +7,7 @@ set(DIRS 254 | usdVolImaging 255 | usdAppUtils 256 | usdviewq 257 | - bin 258 | + #bin 259 | plugin 260 | ) 261 | 262 | -------------------------------------------------------------------------------- /patches/hdrpr.diff: -------------------------------------------------------------------------------- 1 | diff --git a/cmake/defaults/Packages.cmake b/cmake/defaults/Packages.cmake 2 | index 0297547..4cf728a 100644 3 | --- a/cmake/defaults/Packages.cmake 4 | +++ b/cmake/defaults/Packages.cmake 5 | @@ -170,7 +170,7 @@ set(RPR_EXR_EXPORT_ENABLED TRUE) 6 | if(HoudiniUSD_FOUND) 7 | find_exr(OpenEXR OpenEXRCore Iex) 8 | endif() 9 | - 10 | +#[[ 11 | if(NOT OpenEXR_FOUND) 12 | find_exr(Half IlmImf Iex) 13 | if(NOT OpenEXR_FOUND) 14 | @@ -183,6 +183,15 @@ if(NOT OpenEXR_FOUND) 15 | message(FATAL_ERROR "Failed to find Half library") 16 | endif() 17 | endif() 18 | +]] 19 | +# Try and find Imath or fallback to OpenEXR 20 | +# Use ImathConfig.cmake, 21 | +# Refer: https://github.com/AcademySoftwareFoundation/Imath/blob/main/docs/PortingGuide2-3.md#openexrimath-3x-only 22 | +find_package(Imath CONFIG) 23 | +if (NOT Imath_FOUND) 24 | + MESSAGE(STATUS "Imath not found. Looking for OpenEXR instead.") 25 | + find_package(OpenEXR REQUIRED) 26 | +endif() 27 | 28 | # ---------------------------------------------- 29 | 30 | diff --git a/cmake/modules/FindOpenEXR.cmake b/cmake/modules/FindOpenEXR.cmake 31 | index d163d58..7ecdea1 100644 32 | --- a/cmake/modules/FindOpenEXR.cmake 33 | +++ b/cmake/modules/FindOpenEXR.cmake 34 | @@ -22,14 +22,8 @@ 35 | # language governing permissions and limitations under the Apache License. 36 | # 37 | 38 | -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows" AND HoudiniUSD_FOUND) 39 | - set(PATH_PARAMS NO_DEFAULT_PATH NO_SYSTEM_ENVIRONMENT_PATH) 40 | -endif() 41 | - 42 | find_path(OPENEXR_INCLUDE_DIR 43 | -NAMES 44 | OpenEXR/half.h 45 | - Imath/half.h 46 | HINTS 47 | "${OPENEXR_LOCATION}" 48 | "$ENV{OPENEXR_LOCATION}" 49 | @@ -37,7 +31,7 @@ PATH_SUFFIXES 50 | include/ 51 | DOC 52 | "OpenEXR headers path" 53 | -${PATH_PARAMS}) 54 | +) 55 | 56 | if(OPENEXR_INCLUDE_DIR) 57 | set(openexr_config_file "${OPENEXR_INCLUDE_DIR}/OpenEXR/OpenEXRConfig.h") 58 | @@ -62,40 +56,37 @@ if(OPENEXR_INCLUDE_DIR) 59 | endif() 60 | endif() 61 | 62 | -if(NOT OpenEXR_FIND_COMPONENTS) 63 | - set(OpenEXR_FIND_COMPONENTS 64 | - Half 65 | - Iex 66 | - Imath 67 | - IlmImf 68 | - IlmThread 69 | - IlmImfUtil 70 | - IexMath) 71 | -endif() 72 | - 73 | -set(OPENEXR_LIBRARY_VARS) 74 | -set(OPENEXR_LIBRARIES) 75 | -foreach(OPENEXR_LIB ${OpenEXR_FIND_COMPONENTS}) 76 | +foreach(OPENEXR_LIB 77 | + Half 78 | + Iex 79 | + Imath 80 | + IlmImf 81 | + IlmThread 82 | + IlmImfUtil 83 | + IexMath 84 | + OpenEXR 85 | + OpenEXRCore 86 | + OpenEXRUtil 87 | + ) 88 | 89 | # OpenEXR libraries may be suffixed with the version number, so we search 90 | # using both versioned and unversioned names. 91 | find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY 92 | NAMES 93 | ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION} 94 | - ${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s 95 | ${OPENEXR_LIB} 96 | HINTS 97 | "${OPENEXR_LOCATION}" 98 | "$ENV{OPENEXR_LOCATION}" 99 | - "${OPENEXR_LIB_LOCATION}" 100 | PATH_SUFFIXES 101 | lib/ 102 | DOC 103 | "OPENEXR's ${OPENEXR_LIB} library path" 104 | - ${PATH_PARAMS}) 105 | + ) 106 | 107 | - list(APPEND OPENEXR_LIBRARY_VARS OPENEXR_${OPENEXR_LIB}_LIBRARY) 108 | - list(APPEND OPENEXR_LIBRARIES ${OPENEXR_${OPENEXR_LIB}_LIBRARY}) 109 | + if(OPENEXR_${OPENEXR_LIB}_LIBRARY) 110 | + list(APPEND OPENEXR_LIBRARIES ${OPENEXR_${OPENEXR_LIB}_LIBRARY}) 111 | + endif() 112 | endforeach(OPENEXR_LIB) 113 | 114 | # So #include works 115 | @@ -107,7 +98,7 @@ include(FindPackageHandleStandardArgs) 116 | find_package_handle_standard_args(OpenEXR 117 | REQUIRED_VARS 118 | OPENEXR_INCLUDE_DIRS 119 | - ${OPENEXR_LIBRARY_VARS} 120 | + OPENEXR_LIBRARIES 121 | VERSION_VAR 122 | OPENEXR_VERSION 123 | ) 124 | diff --git a/cmake/modules/FindUSDMonolithic.cmake b/cmake/modules/FindUSDMonolithic.cmake 125 | index f0ba4b4..9c62b08 100644 126 | --- a/cmake/modules/FindUSDMonolithic.cmake 127 | +++ b/cmake/modules/FindUSDMonolithic.cmake 128 | @@ -1,6 +1,27 @@ 129 | # USD 20.05 does not generate cmake config for the monolithic library as it does for the default mode 130 | # So we have to handle monolithic USD in a special way 131 | 132 | +# Find Boost package before getting any boost specific components as we need to 133 | +# disable boost-provided cmake config, based on the boost version found. 134 | +find_package(Boost REQUIRED) 135 | + 136 | +# Boost provided cmake files (introduced in boost version 1.70) result in 137 | +# inconsistent build failures on different platforms, when trying to find boost 138 | +# component dependencies like python, program options, etc. Refer some related 139 | +# discussions: 140 | +# https://github.com/boostorg/python/issues/262#issuecomment-483069294 141 | +# https://github.com/boostorg/boost_install/issues/12#issuecomment-508683006 142 | +# 143 | +# Hence to avoid issues with Boost provided cmake config, Boost_NO_BOOST_CMAKE 144 | +# is enabled by default for boost version 1.70 and above. If a user explicitly 145 | +# set Boost_NO_BOOST_CMAKE to Off, following will be a no-op. 146 | +if (${Boost_VERSION_STRING} VERSION_GREATER_EQUAL "1.70") 147 | + option(Boost_NO_BOOST_CMAKE "Disable boost-provided cmake config" ON) 148 | + if (Boost_NO_BOOST_CMAKE) 149 | + message(STATUS "Disabling boost-provided cmake config") 150 | + endif() 151 | +endif() 152 | + 153 | find_path(USD_INCLUDE_DIR pxr/pxr.h 154 | PATHS ${pxr_DIR}/include 155 | $ENV{pxr_DIR}/include 156 | @@ -51,10 +72,7 @@ if(USDMonolithic_FOUND) 157 | # as Boost_VERSION_STRING in CMake 3.14+ 158 | set(boost_version_string "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") 159 | 160 | - if (((${boost_version_string} VERSION_GREATER_EQUAL "1.67") AND 161 | - (${boost_version_string} VERSION_LESS "1.70")) OR 162 | - ((${boost_version_string} VERSION_GREATER_EQUAL "1.70") AND 163 | - Boost_NO_BOOST_CMAKE)) 164 | + if (${Boost_VERSION_STRING} VERSION_GREATER_EQUAL "1.67") 165 | # As of boost 1.67 the boost_python component name includes the 166 | # associated Python version (e.g. python27, python36). After boost 1.70 167 | # the built-in cmake files will deal with this. If we are using boost 168 | @@ -103,7 +121,7 @@ if(USDMonolithic_FOUND) 169 | if(PXR_VERSION GREATER_EQUAL 2111) 170 | set_target_properties(usd_monolithic PROPERTIES 171 | IMPORTED_IMPLIB_RELEASE "${USD_MONOLITHIC_LIBRARY}" 172 | - IMPORTED_LOCATION_RELEASE "${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}usd_usd_ms${CMAKE_SHARED_LIBRARY_SUFFIX}" 173 | + IMPORTED_LOCATION_RELEASE "${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}usd_ms${CMAKE_SHARED_LIBRARY_SUFFIX}" 174 | ) 175 | else() 176 | set_target_properties(usd_monolithic PROPERTIES 177 | diff --git a/pxr/imaging/rprUsd/CMakeLists.txt b/pxr/imaging/rprUsd/CMakeLists.txt 178 | index db2b8ab..9d4f852 100644 179 | --- a/pxr/imaging/rprUsd/CMakeLists.txt 180 | +++ b/pxr/imaging/rprUsd/CMakeLists.txt 181 | @@ -158,6 +158,13 @@ if(PXR_VERSION GREATER_EQUAL 2108) 182 | target_compile_definitions(rprUsd PRIVATE USE_USDSHADE_MTLX) 183 | endif() 184 | 185 | +target_link_libraries(rprUsd ${OPENEXR_LIBRARIES}) 186 | +target_link_libraries(rprUsd ${TBB_LIBRARY}) 187 | + 188 | +target_include_directories(rprUsd PUBLIC ${OPENEXR_INCLUDE_DIR}) 189 | +target_include_directories(rprUsd PUBLIC ${IMATH_INCLUDE_DIR}) 190 | +target_include_directories(rprUsd PUBLIC ${TBB_INCLUDE_DIR}) 191 | + 192 | add_dependencies(rprUsd_headerfiles rprUsdSchema) 193 | 194 | if(HoudiniUSD_FOUND) 195 | diff --git a/pxr/imaging/rprUsd/hdMtlxFixed.cpp b/pxr/imaging/rprUsd/hdMtlxFixed.cpp 196 | index 4ddc559..abacac6 100644 197 | --- a/pxr/imaging/rprUsd/hdMtlxFixed.cpp 198 | +++ b/pxr/imaging/rprUsd/hdMtlxFixed.cpp 199 | @@ -445,7 +445,7 @@ HdMtlxCreateMtlxDocumentFromHdNetwork_Fixed( 200 | HdMaterialNode2 const* hdMaterialXSurfaceNodePtr, 201 | HdMaterialNode2 const* hdMaterialXDisplacementNodePtr, 202 | SdfPath const& materialPath, 203 | - mx::DocumentPtr const& libraries, 204 | + mx::ConstDocumentPtr const& libraries, 205 | std::set * hdTextureNodes, // Paths to the Hd Texture Nodes 206 | mx::StringMap * mxHdTextureMap) // Mx-Hd texture name counterparts 207 | { 208 | diff --git a/pxr/imaging/rprUsd/materialRegistry.cpp b/pxr/imaging/rprUsd/materialRegistry.cpp 209 | index bd2c7dc..7a862ed 100644 210 | --- a/pxr/imaging/rprUsd/materialRegistry.cpp 211 | +++ b/pxr/imaging/rprUsd/materialRegistry.cpp 212 | @@ -33,6 +33,10 @@ limitations under the License. 213 | #include "pxr/usd/usdShade/tokens.h" 214 | #include "pxr/imaging/hd/sceneDelegate.h" 215 | 216 | +#ifdef USE_USDSHADE_MTLX 217 | +#include "pxr/usd/usdMtlx/utils.h" 218 | +#endif 219 | + 220 | #include "materialNodes/usdNode.h" 221 | #include "materialNodes/mtlxNode.h" 222 | #include "materialNodes/rprApiMtlxNode.h" 223 | @@ -56,7 +60,7 @@ HdMtlxCreateMtlxDocumentFromHdNetwork_Fixed( 224 | HdMaterialNode2 const* hdMaterialXSurfaceNodePtr, 225 | HdMaterialNode2 const* hdMaterialXDisplacementNodePtr, 226 | SdfPath const& materialPath, 227 | - mx::DocumentPtr const& libraries, 228 | + mx::ConstDocumentPtr const& libraries, 229 | std::set* hdTextureNodes, 230 | mx::StringMap* mxHdTextureMap); 231 | #else 232 | @@ -428,7 +432,7 @@ HdMaterialNode2 const* GetTerminalNodeByToken(SdfPath const& materialPath, RprUs 233 | RprUsdMaterial* CreateMaterialXFromUsdShade( 234 | SdfPath const& materialPath, 235 | RprUsd_MaterialBuilderContext const& context, 236 | - mx::DocumentPtr& stdLibraries, 237 | + mx::ConstDocumentPtr& stdLibraries, 238 | bool enableDisplacement) { 239 | 240 | #ifdef USE_USDSHADE_MTLX 241 | @@ -445,27 +449,7 @@ RprUsdMaterial* CreateMaterialXFromUsdShade( 242 | 243 | // TODO: move lib initialization to class constructor 244 | if (!stdLibraries) { 245 | - std::string materialXStdlibPath; 246 | - 247 | - const TfType schemaBaseType = TfType::Find(); 248 | - PlugPluginPtr usdPlugin = PlugRegistry::GetInstance().GetPluginForType(schemaBaseType); 249 | - if (usdPlugin) { 250 | - std::string usdLibPath = usdPlugin->GetPath(); 251 | - std::string usdDir = TfNormPath(TfGetPathName(usdLibPath) + ".."); 252 | - materialXStdlibPath = usdDir; 253 | -#ifdef __APPLE__ 254 | - materialXStdlibPath += "/Resources"; 255 | -#endif 256 | - } 257 | - 258 | - stdLibraries = mx::createDocument(); 259 | - 260 | - if (!materialXStdlibPath.empty()) { 261 | - mx::FilePathVec libraryFolders = {"libraries"}; 262 | - mx::FileSearchPath searchPath; 263 | - searchPath.append(mx::FilePath(materialXStdlibPath)); 264 | - mx::loadLibraries(libraryFolders, searchPath, stdLibraries); 265 | - } 266 | + stdLibraries = UsdMtlxGetDocument(""); 267 | } 268 | 269 | MaterialX::StringMap textureMap; 270 | diff --git a/pxr/imaging/rprUsd/materialRegistry.h b/pxr/imaging/rprUsd/materialRegistry.h 271 | index 3ded4e5..f3c9b00 100644 272 | --- a/pxr/imaging/rprUsd/materialRegistry.h 273 | +++ b/pxr/imaging/rprUsd/materialRegistry.h 274 | @@ -149,7 +149,7 @@ private: 275 | std::unique_ptr m_mtlxLoader; 276 | #endif 277 | 278 | - MaterialX::DocumentPtr m_stdLibraries; 279 | + MaterialX::ConstDocumentPtr m_stdLibraries; 280 | 281 | std::vector> m_mtlxInfos; 282 | bool m_mtlxDefsDirty = true; 283 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2023 Advanced Micro Devices, Inc 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/hydrarpr/ui.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import bpy 16 | 17 | from .engine import RPRHydraRenderEngine 18 | 19 | 20 | class Panel(bpy.types.Panel): 21 | bl_space_type = 'PROPERTIES' 22 | bl_region_type = 'WINDOW' 23 | bl_context = 'render' 24 | COMPAT_ENGINES = {RPRHydraRenderEngine.bl_idname} 25 | 26 | @classmethod 27 | def poll(cls, context): 28 | return context.engine in cls.COMPAT_ENGINES 29 | 30 | 31 | class RPR_HYDRA_RENDER_PT_final(Panel): 32 | bl_idname = 'RPR_HYDRA_RENDER_PT_final' 33 | bl_label = "RPR Final Settings" 34 | 35 | def draw(self, context): 36 | settings = context.scene.hydra_rpr.final 37 | 38 | layout = self.layout 39 | layout.use_property_split = True 40 | layout.use_property_decorate = False 41 | 42 | col = layout.column() 43 | col.prop(settings, "render_quality") 44 | col.prop(settings, "render_mode") 45 | 46 | 47 | class FinalPanel(bpy.types.Panel): 48 | bl_parent_id = RPR_HYDRA_RENDER_PT_final.bl_idname 49 | bl_space_type = 'PROPERTIES' 50 | bl_region_type = 'WINDOW' 51 | bl_options = {'DEFAULT_CLOSED'} 52 | 53 | def settings(self, context): 54 | return context.scene.hydra_rpr.final 55 | 56 | 57 | class RPR_HYDRA_RENDER_PT_samples_final(FinalPanel): 58 | bl_label = "Samples" 59 | 60 | def draw(self, context): 61 | layout = self.layout 62 | layout.use_property_split = True 63 | layout.use_property_decorate = False 64 | 65 | settings = self.settings(context) 66 | layout.prop(settings, "max_samples") 67 | 68 | col = layout.column(align=True) 69 | col.prop(settings, "variance_threshold") 70 | row = col.row() 71 | row.enabled = settings.variance_threshold > 0.0 72 | row.prop(settings, "min_adaptive_samples") 73 | 74 | 75 | class RPR_HYDRA_RENDER_PT_quality_final(FinalPanel): 76 | bl_label = "Quality" 77 | 78 | def draw(self, context): 79 | layout = self.layout 80 | layout.use_property_split = True 81 | layout.use_property_decorate = False 82 | 83 | quality = self.settings(context).quality 84 | 85 | col = layout.column(align=True) 86 | col.prop(quality, "max_ray_depth") 87 | col.prop(quality, "max_ray_depth_diffuse") 88 | col.prop(quality, "max_ray_depth_glossy") 89 | col.prop(quality, "max_ray_depth_refraction") 90 | col.prop(quality, "max_ray_depth_glossy_refraction") 91 | 92 | layout.prop(quality, "raycast_epsilon") 93 | layout.prop(quality, "radiance_clamping") 94 | 95 | 96 | class RPR_HYDRA_RENDER_PT_denoise_final(FinalPanel): 97 | bl_label = "" 98 | 99 | def draw_header(self, context): 100 | self.layout.prop(self.settings(context).denoise, "enable") 101 | 102 | def draw(self, context): 103 | layout = self.layout 104 | layout.use_property_split = True 105 | layout.use_property_decorate = False 106 | 107 | denoise = self.settings(context).denoise 108 | 109 | layout.enabled = denoise.enable 110 | layout.prop(denoise, "min_iter") 111 | layout.prop(denoise, "iter_step") 112 | 113 | 114 | class RPR_HYDRA_RENDER_PT_film_final(FinalPanel): 115 | bl_label = "Film" 116 | 117 | def draw(self, context): 118 | layout = self.layout 119 | layout.use_property_split = True 120 | layout.use_property_decorate = False 121 | 122 | layout.prop(self.settings(context), "enable_alpha", text="Transparent Background") 123 | 124 | 125 | class RPR_HYDRA_RENDER_PT_pixel_filter_final(FinalPanel): 126 | bl_label = "Pixel Filter" 127 | 128 | @classmethod 129 | def poll(cls, context): 130 | return context.scene.hydra_rpr.viewport.render_quality == 'Northstar' 131 | 132 | def draw(self, context): 133 | layout = self.layout 134 | layout.use_property_split = True 135 | layout.use_property_decorate = False 136 | 137 | layout.prop(self.settings(context).quality, "pixel_filter_width") 138 | 139 | # 140 | # VIEWPORT RENDER SETTINGS 141 | # 142 | class RPR_HYDRA_RENDER_PT_viewport(Panel): 143 | bl_idname = 'RPR_HYDRA_RENDER_PT_viewport' 144 | bl_label = "RPR Viewport Settings" 145 | 146 | def draw(self, context): 147 | layout = self.layout 148 | layout.use_property_split = True 149 | layout.use_property_decorate = False 150 | 151 | settings = context.scene.hydra_rpr.viewport 152 | layout.prop(settings, "render_quality") 153 | layout.prop(settings, "render_mode") 154 | 155 | 156 | class ViewportPanel(bpy.types.Panel): 157 | bl_parent_id = RPR_HYDRA_RENDER_PT_viewport.bl_idname 158 | bl_space_type = 'PROPERTIES' 159 | bl_region_type = 'WINDOW' 160 | bl_options = {'DEFAULT_CLOSED'} 161 | 162 | def settings(self, context): 163 | return context.scene.hydra_rpr.viewport 164 | 165 | 166 | class RPR_HYDRA_RENDER_PT_samples_viewport(ViewportPanel): 167 | bl_label = "Samples" 168 | 169 | def draw(self, context): 170 | layout = self.layout 171 | layout.use_property_split = True 172 | layout.use_property_decorate = False 173 | 174 | settings = self.settings(context) 175 | layout.prop(settings, "max_samples") 176 | 177 | col = layout.column(align=True) 178 | col.prop(settings, "variance_threshold") 179 | row = col.row() 180 | row.enabled = settings.variance_threshold > 0.0 181 | row.prop(settings, "min_adaptive_samples") 182 | 183 | 184 | class RPR_HYDRA_RENDER_PT_quality_viewport(ViewportPanel): 185 | bl_label = "Quality" 186 | 187 | def draw(self, context): 188 | layout = self.layout 189 | layout.use_property_split = True 190 | layout.use_property_decorate = False 191 | 192 | quality = self.settings(context).interactive_quality 193 | layout.prop(quality, "max_ray_depth") 194 | layout.prop(quality, "enable_downscale") 195 | layout.prop(quality, "resolution_downscale") 196 | 197 | 198 | class RPR_HYDRA_RENDER_PT_denoise_viewport(ViewportPanel): 199 | bl_label = "" 200 | 201 | def draw_header(self, context): 202 | self.layout.prop(self.settings(context).denoise, "enable") 203 | 204 | def draw(self, context): 205 | layout = self.layout 206 | layout.use_property_split = True 207 | layout.use_property_decorate = False 208 | 209 | denoise = self.settings(context).denoise 210 | layout.enabled = denoise.enable 211 | layout.prop(denoise, "min_iter") 212 | layout.prop(denoise, "iter_step") 213 | 214 | 215 | class RPR_HYDRA_RENDER_PT_pixel_filter_viewport(ViewportPanel): 216 | bl_label = "Pixel Filter" 217 | 218 | @classmethod 219 | def poll(cls, context): 220 | return context.scene.hydra_rpr.viewport.render_quality == 'Northstar' 221 | 222 | def draw(self, context): 223 | self.layout.use_property_split = True 224 | self.layout.use_property_decorate = False 225 | 226 | col = self.layout.column() 227 | col.prop(self.settings(context).quality, "pixel_filter_width") 228 | 229 | 230 | class RPR_HYDRA_LIGHT_PT_light(Panel): 231 | """ 232 | Physical light sources 233 | """ 234 | bl_label = "Light" 235 | bl_context = 'data' 236 | 237 | @classmethod 238 | def poll(cls, context): 239 | return super().poll(context) and context.light 240 | 241 | def draw(self, context): 242 | layout = self.layout 243 | 244 | light = context.light 245 | 246 | layout.prop(light, "type", expand=True) 247 | 248 | layout.use_property_split = True 249 | layout.use_property_decorate = False 250 | 251 | main_col = layout.column() 252 | 253 | main_col.prop(light, "color") 254 | main_col.prop(light, "energy") 255 | main_col.separator() 256 | 257 | if light.type == 'POINT': 258 | row = main_col.row(align=True) 259 | row.prop(light, "shadow_soft_size", text="Radius") 260 | 261 | elif light.type == 'SPOT': 262 | col = main_col.column(align=True) 263 | col.prop(light, 'spot_size', slider=True) 264 | col.prop(light, 'spot_blend', slider=True) 265 | 266 | main_col.prop(light, 'show_cone') 267 | 268 | elif light.type == 'SUN': 269 | main_col.prop(light, "angle") 270 | 271 | elif light.type == 'AREA': 272 | main_col.prop(light, "shape", text="Shape") 273 | sub = main_col.column(align=True) 274 | 275 | if light.shape in {'SQUARE', 'DISK'}: 276 | sub.prop(light, "size") 277 | elif light.shape in {'RECTANGLE', 'ELLIPSE'}: 278 | sub.prop(light, "size", text="Size X") 279 | sub.prop(light, "size_y", text="Y") 280 | 281 | else: 282 | main_col.prop(light, 'size') 283 | 284 | 285 | class RPR_HYDRA_RENDER_PT_passes(Panel): 286 | bl_label = "Passes" 287 | bl_context = "view_layer" 288 | 289 | def draw(self, context): 290 | pass 291 | 292 | 293 | class RPR_HYDRA_RENDER_PT_passes_data(Panel): 294 | bl_label = "Data" 295 | bl_context = "view_layer" 296 | bl_parent_id = "RPR_HYDRA_RENDER_PT_passes" 297 | 298 | def draw(self, context): 299 | layout = self.layout 300 | layout.use_property_split = True 301 | layout.use_property_decorate = False 302 | 303 | view_layer = context.view_layer 304 | 305 | col = layout.column(heading="Include", align=True) 306 | col.prop(view_layer, "use_pass_z") 307 | col.prop(view_layer, "use_pass_normal") 308 | col.prop(view_layer, "use_pass_position") 309 | 310 | 311 | register_classes, unregister_classes = bpy.utils.register_classes_factory(( 312 | RPR_HYDRA_RENDER_PT_final, 313 | RPR_HYDRA_RENDER_PT_samples_final, 314 | RPR_HYDRA_RENDER_PT_quality_final, 315 | RPR_HYDRA_RENDER_PT_denoise_final, 316 | RPR_HYDRA_RENDER_PT_film_final, 317 | RPR_HYDRA_RENDER_PT_pixel_filter_final, 318 | 319 | RPR_HYDRA_RENDER_PT_viewport, 320 | RPR_HYDRA_RENDER_PT_samples_viewport, 321 | RPR_HYDRA_RENDER_PT_quality_viewport, 322 | RPR_HYDRA_RENDER_PT_denoise_viewport, 323 | RPR_HYDRA_RENDER_PT_pixel_filter_viewport, 324 | 325 | RPR_HYDRA_LIGHT_PT_light, 326 | 327 | RPR_HYDRA_RENDER_PT_passes, 328 | RPR_HYDRA_RENDER_PT_passes_data, 329 | )) 330 | 331 | 332 | def get_panels(): 333 | # follow the Cycles model of excluding panels we don't want 334 | exclude_panels = { 335 | 'RENDER_PT_stamp', 336 | 'DATA_PT_light', 337 | 'DATA_PT_spot', 338 | 'NODE_DATA_PT_light', 339 | 'DATA_PT_falloff_curve', 340 | 'RENDER_PT_post_processing', 341 | 'RENDER_PT_simplify', 342 | 'SCENE_PT_audio', 343 | 'RENDER_PT_freestyle' 344 | } 345 | include_eevee_panels = { 346 | 'MATERIAL_PT_preview', 347 | 'EEVEE_MATERIAL_PT_context_material', 348 | 'EEVEE_MATERIAL_PT_surface', 349 | 'EEVEE_MATERIAL_PT_volume', 350 | 'EEVEE_MATERIAL_PT_settings', 351 | 'EEVEE_WORLD_PT_surface', 352 | } 353 | include_hydra_panels = { 354 | "RENDER_PT_hydra_debug", 355 | } 356 | 357 | for panel_cls in bpy.types.Panel.__subclasses__(): 358 | if hasattr(panel_cls, 'COMPAT_ENGINES') and ( 359 | ('BLENDER_RENDER' in panel_cls.COMPAT_ENGINES and panel_cls.__name__ not in exclude_panels) or 360 | ('BLENDER_EEVEE' in panel_cls.COMPAT_ENGINES and panel_cls.__name__ in include_eevee_panels) or 361 | (panel_cls.__name__ in include_hydra_panels) 362 | ): 363 | yield panel_cls 364 | 365 | 366 | def register(): 367 | register_classes() 368 | 369 | for panel_cls in get_panels(): 370 | panel_cls.COMPAT_ENGINES.add(RPRHydraRenderEngine.bl_idname) 371 | 372 | 373 | def unregister(): 374 | unregister_classes() 375 | 376 | for panel_cls in get_panels(): 377 | if RPRHydraRenderEngine.bl_idname in panel_cls.COMPAT_ENGINES: 378 | panel_cls.COMPAT_ENGINES.remove(RPRHydraRenderEngine.bl_idname) 379 | -------------------------------------------------------------------------------- /src/hydrarpr/properties.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************** 2 | # Copyright 2023 Advanced Micro Devices, Inc 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ******************************************************************** 15 | import math 16 | import platform 17 | 18 | import bpy 19 | from bpy.props import ( 20 | PointerProperty, 21 | EnumProperty, 22 | FloatProperty, 23 | BoolProperty, 24 | IntProperty, 25 | StringProperty, 26 | ) 27 | 28 | 29 | class Properties(bpy.types.PropertyGroup): 30 | bl_type = None 31 | 32 | @classmethod 33 | def register(cls): 34 | cls.bl_type.hydra_rpr = bpy.props.PointerProperty( 35 | name="Hydra RPR", 36 | description="Hydra RPR properties", 37 | type=cls, 38 | ) 39 | 40 | @classmethod 41 | def unregister(cls): 42 | del cls.bl_type.hydra_rpr 43 | 44 | 45 | class QualitySettings(bpy.types.PropertyGroup): 46 | max_ray_depth: IntProperty( 47 | name="Max Ray Depth", 48 | description="The number of times that a ray bounces off various surfaces " 49 | "before being terminated", 50 | min=1, max=50, 51 | default=8, 52 | ) 53 | max_ray_depth_diffuse: IntProperty( 54 | name="Diffuse Ray Depth", 55 | description="The maximum number of times that a light ray can be bounced off diffuse surfaces", 56 | min=0, max=50, 57 | default=3, 58 | ) 59 | max_ray_depth_glossy: IntProperty( 60 | name="Glossy Ray Depth", 61 | description="The maximum number of ray bounces from specular surfaces", 62 | min=0, max=50, 63 | default=3, 64 | ) 65 | max_ray_depth_refraction: IntProperty( 66 | name="Refraction Ray Depth", 67 | description="The maximum number of times that a light ray can be refracted, and is\n" 68 | "designated for clear transparent materials, such as glass", 69 | min=0, max=50, 70 | default=3, 71 | ) 72 | max_ray_depth_glossy_refraction: IntProperty( 73 | name="Glossy Refraction Ray Depth", 74 | description="The Glossy Refraction Ray Depth parameter is similar to the Refraction Ray Depth.\n" 75 | "The difference is that it is aimed to work with matte refractive materials,\n" 76 | "such as semi-frosted glass", 77 | min=0, max=50, 78 | default=3, 79 | ) 80 | max_ray_depth_shadow: IntProperty( 81 | name="Shadow Ray Depth", 82 | description="Controls the accuracy of shadows cast by transparent objects.\n" 83 | "It defines the maximum number of surfaces that a light ray can encounter on\n" 84 | "its way causing these surfaces to cast shadows", 85 | min=0, max=50, 86 | default=2, 87 | ) 88 | raycast_epsilon: FloatProperty( 89 | name="Ray Cast Epsilon", 90 | description="Determines an offset used to move light rays away from the geometry for\n" 91 | "ray-surface intersection calculations", 92 | subtype='DISTANCE', 93 | min=1e-6, max=1.0, 94 | default=2e-3, 95 | ) 96 | enable_radiance_clamping: BoolProperty( 97 | name="Clamp Fireflies", 98 | description="Clamp Fireflies", 99 | default=False, 100 | ) 101 | radiance_clamping: FloatProperty( 102 | name="Max Radiance", 103 | description="Limits the intensity or the maximum brightness of samples in the scene.\n" 104 | "Greater clamp radiance values produce more brightness. Set to 0 ot disable clamping", 105 | min=0.0, max=1e6, 106 | default=0.0, 107 | ) 108 | pixel_filter_width: FloatProperty( 109 | name="Width", 110 | description="Pixel filter width", 111 | min=0.0, max=5.0, 112 | default=1.5, 113 | ) 114 | 115 | 116 | class InteractiveQualitySettings(bpy.types.PropertyGroup): 117 | max_ray_depth: IntProperty( 118 | name="Max Ray Depth", 119 | description="Controls value of 'Max Ray Depth' in interactive mode", 120 | min=1, max=50, 121 | default=2, 122 | ) 123 | enable_downscale: BoolProperty( 124 | name="Downscale Resolution", 125 | description="Controls whether in interactive mode resolution should be downscaled or no", 126 | default=True, 127 | ) 128 | resolution_downscale: IntProperty( 129 | name="Resolution Downscale", 130 | description="Controls how much rendering resolution is downscaled in interactive mode.\n" 131 | "Formula: resolution / (2 ^ downscale). E.g. downscale==2 will give you 4 times\n" 132 | "smaller rendering resolution", 133 | min=0, max=10, 134 | default=3, 135 | ) 136 | 137 | 138 | class ContourSettings(bpy.types.PropertyGroup): 139 | antialiasing: FloatProperty( 140 | name="Antialiasing", 141 | description="Contour Antialising", 142 | min=0.0, max=1.0, 143 | default=1.0, 144 | ) 145 | use_normal: BoolProperty( 146 | name="Use Normal", 147 | description="Whether to use geometry normals for edge detection or not", 148 | default=True, 149 | ) 150 | line_width_normal: FloatProperty( 151 | name="Line Width Normal", 152 | description="Line width of edges detected via normals", 153 | min=1.0, max=100.0, 154 | default=1.0, 155 | ) 156 | normal_threshold: FloatProperty( 157 | name="Normal Threshold", 158 | description="Threshold for normals, in degrees", 159 | subtype='ANGLE', 160 | min=0.0, max=math.radians(180.0), 161 | default=math.radians(45.0), 162 | ) 163 | use_prim_id: BoolProperty( 164 | name="Use Primitive ID", 165 | description="Whether to use primitive Id for edge detection or not", 166 | default=True, 167 | ) 168 | line_width_prim_id: FloatProperty( 169 | name="Line Primitive Id", 170 | description="Line width of edges detected via primitive Id", 171 | min=0.0, max=100.0, 172 | default=1.0, 173 | ) 174 | use_material_id: BoolProperty( 175 | name="Use Material Id", 176 | description="Whether to use material Id for edge detection or not", 177 | default=True, 178 | ) 179 | line_width_material_id: FloatProperty( 180 | name="Line Width Material Id", 181 | description="Line width of edges detected via material Id", 182 | min=0.0, max=100.0, 183 | default=1.0, 184 | ) 185 | debug: BoolProperty( 186 | name="Debug", 187 | description="""Whether to show colored outlines according to used features or not. 188 | Colors legend: 189 | * red - primitive Id 190 | * green - material Id 191 | * blue - normal 192 | * yellow - primitive Id + material Id 193 | * magenta - primitive Id + normal 194 | * cyan - material Id + normal 195 | * black - all""", 196 | default=True, 197 | ) 198 | 199 | 200 | class DenoiseSettings(bpy.types.PropertyGroup): 201 | enable: BoolProperty( 202 | name="AI Denoising", 203 | description="Enable AI Denoising", 204 | default=False, 205 | ) 206 | min_iter: IntProperty( 207 | name="Min Iteration", 208 | description="The first iteration on which denoising should be applied", 209 | min=1, max=2 ** 16, 210 | default=4, 211 | ) 212 | iter_step: IntProperty( 213 | name="Iteration Step", 214 | description="Denoise use frequency. To denoise on each iteration, set to 1", 215 | min=1, max=2 ** 16, 216 | default=32, 217 | ) 218 | 219 | 220 | class RenderSettings(bpy.types.PropertyGroup): 221 | device: EnumProperty( 222 | name="Render Device", 223 | description="Render Device", 224 | items=(('GPU', "GPU", "GPU render device"), 225 | ('CPU', "CPU", "Legacy render device")), 226 | default='GPU', 227 | ) 228 | render_quality: EnumProperty( 229 | name="Render Quality", 230 | description="Render Quality", 231 | items=(('Northstar', "Full", "Full render quality"), 232 | ('HybridPro', "Interactive", "Interactive render quality")), 233 | default='Northstar', 234 | ) 235 | render_mode: EnumProperty( 236 | name="Render Mode", 237 | description="Override render mode", 238 | items=( 239 | ('Global Illumination', "Global Illumination", "Global illumination render mode"), 240 | ('Direct Illumination', "Direct Illumination", "Direct illumination render mode"), 241 | ('Wireframe', "Wireframe", "Wireframe render mode"), 242 | ('Material Index', "Material Index", "Material index render mode"), 243 | ('Position', "Position", "World position render mode"), 244 | ('Normal', "Normal", "Shading normal render mode"), 245 | ('Texcoord', "Texture Coordinate", "Texture coordinate render mode"), 246 | ('Ambient Occlusion', "Ambient Occlusion", "Ambient occlusion render mode"), 247 | ('Diffuse', "Diffuse", "Diffuse render mode"), 248 | ('Contour', "Contour", "Contour render mode"), 249 | ), 250 | default='Global Illumination', 251 | ) 252 | ao_radius: FloatProperty( 253 | name="AO Radius", 254 | description="Ambient Occlusion Radius", 255 | min=0.0, max=100.0, 256 | default=1.0, 257 | ) 258 | max_samples: IntProperty( 259 | name="Max Samples", 260 | description="Maximum number of samples to render for each pixel", 261 | min=1, max=2 ** 16, 262 | default=256, 263 | ) 264 | min_adaptive_samples: IntProperty( 265 | name="Min Samples", 266 | description="Minimum number of samples to render for each pixel. After this, adaptive sampling\n" 267 | "will stop sampling pixels where noise is less than 'Variance Threshold'", 268 | min=1, max=2 ** 16, 269 | default=64, 270 | ) 271 | variance_threshold: FloatProperty( 272 | name="Noise Threshold", 273 | description="Cutoff for adaptive sampling. Once pixels are below this amount of noise,\n" 274 | "no more samples are added. Set to 0 for no cutoff", 275 | min=0.0, max=1.0, 276 | default=0.05, 277 | ) 278 | enable_alpha: BoolProperty( 279 | name="Enable Color Alpha", 280 | description="World background is transparent, for compositing the render over another background", 281 | default=False, 282 | ) 283 | enable_motion_blur: BoolProperty( 284 | name="Enable Beauty Motion Blur", 285 | description="If disabled, only velocity AOV will store information about movement on the scene.\n" 286 | "Required for motion blur that is generated in post-processing", 287 | default=True, 288 | ) 289 | 290 | quality: PointerProperty(type=QualitySettings) 291 | interactive_quality: PointerProperty(type=InteractiveQualitySettings) 292 | 293 | contour: PointerProperty(type=ContourSettings) 294 | denoise: PointerProperty(type=DenoiseSettings) 295 | 296 | 297 | class RenderStudioSettings(bpy.types.PropertyGroup): 298 | def live_sync_update(self, context): 299 | from .render_studio.resolver import rs_resolver 300 | if not self.live_sync and rs_resolver.is_live_sync: 301 | rs_resolver.stop_live_sync() 302 | 303 | live_sync: BoolProperty( 304 | name="Live Sync", 305 | description="Enable live syncing mode", 306 | default=False, 307 | update=live_sync_update, 308 | ) 309 | channel: StringProperty( 310 | name="Channel", 311 | description="Syncing Channel: directory to which the files will be synchronized", 312 | default=f"Blender/{platform.node()}", 313 | ) 314 | filename: StringProperty( 315 | name="Custom File Name", 316 | description="The name of the synced Usd file: live empty to use current scene name", 317 | default="", 318 | ) 319 | 320 | # Blender Usd export settings 321 | selected_objects_only: BoolProperty( 322 | name="Selection Only", 323 | default=False, 324 | ) 325 | visible_objects_only: BoolProperty( 326 | name="Visible Only", 327 | default=True, 328 | ) 329 | export_animation: BoolProperty( 330 | name="Animation", 331 | default=False, 332 | ) 333 | export_hair: BoolProperty( 334 | name="Hair", 335 | default=False, 336 | ) 337 | export_uvmaps: BoolProperty( 338 | name="UV Maps", 339 | default=True, 340 | ) 341 | export_normals: BoolProperty( 342 | name="Normals", 343 | default=True, 344 | ) 345 | export_world: BoolProperty( 346 | name="World", 347 | default=True, 348 | ) 349 | export_materials: BoolProperty( 350 | name="Materials", 351 | default=True, 352 | ) 353 | root_prim_path: StringProperty( 354 | name="Root Prim", 355 | default="", 356 | ) 357 | generate_preview_surface: BoolProperty( 358 | name="To Usd Preview", 359 | default=True, 360 | ) 361 | export_textures: BoolProperty( 362 | name="Export Textures", 363 | default=True, 364 | ) 365 | overwrite_textures: BoolProperty( 366 | name="Overwrite Textures", 367 | default=False, 368 | ) 369 | use_instancing: BoolProperty( 370 | name="Instancing", 371 | default=False, 372 | ) 373 | evaluation_mode: EnumProperty( 374 | name="Use Settings for", 375 | items=(('RENDER', "Render", "Render evaluation mode"), 376 | ('VIEWPORT', "Viewport", "Viewport evaluation mode")), 377 | default='RENDER', 378 | ) 379 | 380 | 381 | class SceneProperties(Properties): 382 | bl_type = bpy.types.Scene 383 | 384 | final: bpy.props.PointerProperty(type=RenderSettings) 385 | viewport: bpy.props.PointerProperty(type=RenderSettings) 386 | render_studio: bpy.props.PointerProperty(type=RenderStudioSettings) 387 | 388 | 389 | register, unregister = bpy.utils.register_classes_factory(( 390 | ContourSettings, 391 | DenoiseSettings, 392 | InteractiveQualitySettings, 393 | QualitySettings, 394 | RenderSettings, 395 | RenderStudioSettings, 396 | SceneProperties, 397 | )) 398 | -------------------------------------------------------------------------------- /patches/usd_opengl_errors_fix.diff: -------------------------------------------------------------------------------- 1 | Subject: [PATCH] opengl 2 | --- 3 | Index: pxr/imaging/hgiGL/capabilities.cpp 4 | IDEA additional info: 5 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 6 | <+>UTF-8 7 | =================================================================== 8 | diff --git a/pxr/imaging/hgiGL/capabilities.cpp b/pxr/imaging/hgiGL/capabilities.cpp 9 | --- a/pxr/imaging/hgiGL/capabilities.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 10 | +++ b/pxr/imaging/hgiGL/capabilities.cpp (date 1691662966555) 11 | @@ -57,6 +57,7 @@ 12 | HgiGLCapabilities::HgiGLCapabilities() 13 | : _glVersion(0) 14 | , _glslVersion(_DefaultGLSLVersion) 15 | + , _coreProfile(false) 16 | { 17 | _LoadCapabilities(); 18 | } 19 | @@ -131,6 +132,11 @@ 20 | &uniformBufferOffsetAlignment); 21 | _uniformBufferOffsetAlignment = uniformBufferOffsetAlignment; 22 | } 23 | + if (_glVersion >= 320) { 24 | + GLint profileMask = 0; 25 | + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask); 26 | + _coreProfile = (profileMask & GL_CONTEXT_CORE_PROFILE_BIT); 27 | + } 28 | if (_glVersion >= 430) { 29 | GLint maxShaderStorageBlockSize = 0; 30 | glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, 31 | @@ -259,4 +265,9 @@ 32 | return _glslVersion; 33 | } 34 | 35 | +bool 36 | +HgiGLCapabilities::GetCoreProfile() const { 37 | + return _coreProfile; 38 | +} 39 | + 40 | PXR_NAMESPACE_CLOSE_SCOPE 41 | Index: pxr/imaging/hgiGL/scopedStateHolder.cpp 42 | IDEA additional info: 43 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 44 | <+>UTF-8 45 | =================================================================== 46 | diff --git a/pxr/imaging/hgiGL/scopedStateHolder.cpp b/pxr/imaging/hgiGL/scopedStateHolder.cpp 47 | --- a/pxr/imaging/hgiGL/scopedStateHolder.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 48 | +++ b/pxr/imaging/hgiGL/scopedStateHolder.cpp (date 1691662966588) 49 | @@ -26,6 +26,7 @@ 50 | #include "pxr/imaging/hgiGL/scopedStateHolder.h" 51 | #include "pxr/imaging/hgiGL/conversions.h" 52 | #include "pxr/imaging/hgiGL/diagnostic.h" 53 | +#include "pxr/imaging/hgiGL/hgi.h" 54 | 55 | #include "pxr/base/trace/trace.h" 56 | #include "pxr/base/tf/diagnostic.h" 57 | @@ -33,8 +34,10 @@ 58 | 59 | PXR_NAMESPACE_OPEN_SCOPE 60 | 61 | -HgiGL_ScopedStateHolder::HgiGL_ScopedStateHolder() 62 | - : _restoreRenderBuffer(0) 63 | +HgiGL_ScopedStateHolder::HgiGL_ScopedStateHolder( 64 | + HgiCapabilities const& capabilities) 65 | + : _coreProfile(capabilities.GetCoreProfile()) 66 | + , _restoreRenderBuffer(0) 67 | , _restoreVao(0) 68 | , _restoreDepthTest(false) 69 | , _restoreDepthWriteMask(false) 70 | @@ -115,7 +118,9 @@ 71 | glGetBooleanv( 72 | GL_SAMPLE_ALPHA_TO_ONE, 73 | (GLboolean*)&_restoreSampleAlphaToOne); 74 | - glGetFloatv(GL_LINE_WIDTH, &_lineWidth); 75 | + if (!_coreProfile) { 76 | + glGetFloatv(GL_LINE_WIDTH, &_lineWidth); 77 | + } 78 | glGetBooleanv(GL_CULL_FACE, (GLboolean*)&_cullFace); 79 | glGetIntegerv(GL_CULL_FACE_MODE, &_cullMode); 80 | glGetIntegerv(GL_FRONT_FACE, &_frontFace); 81 | @@ -139,7 +144,9 @@ 82 | } 83 | 84 | glGetBooleanv(GL_MULTISAMPLE, (GLboolean*)&_restoreMultiSample); 85 | - glGetBooleanv(GL_POINT_SMOOTH, (GLboolean*)&_restorePointSmooth); 86 | + if (!_coreProfile) { 87 | + glGetBooleanv(GL_POINT_SMOOTH, (GLboolean*)&_restorePointSmooth); 88 | + } 89 | 90 | HGIGL_POST_PENDING_GL_ERRORS(); 91 | #if defined(GL_KHR_debug) 92 | @@ -235,7 +242,9 @@ 93 | _restoreViewport[2], _restoreViewport[3]); 94 | glBindVertexArray(_restoreVao); 95 | glBindRenderbuffer(GL_RENDERBUFFER, _restoreRenderBuffer); 96 | - glLineWidth(_lineWidth); 97 | + if (!_coreProfile) { 98 | + glLineWidth(_lineWidth); 99 | + } 100 | if (_cullFace) { 101 | glEnable(GL_CULL_FACE); 102 | } else { 103 | @@ -285,10 +294,12 @@ 104 | glDisable(GL_MULTISAMPLE); 105 | } 106 | 107 | - if (_restorePointSmooth) { 108 | - glEnable(GL_POINT_SMOOTH); 109 | - } else { 110 | - glDisable(GL_POINT_SMOOTH); 111 | + if (!_coreProfile) { 112 | + if (_restorePointSmooth) { 113 | + glEnable(GL_POINT_SMOOTH); 114 | + } else { 115 | + glDisable(GL_POINT_SMOOTH); 116 | + } 117 | } 118 | 119 | static const GLuint samplers[8] = {0}; 120 | Index: pxr/imaging/hgiGL/capabilities.h 121 | IDEA additional info: 122 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 123 | <+>UTF-8 124 | =================================================================== 125 | diff --git a/pxr/imaging/hgiGL/capabilities.h b/pxr/imaging/hgiGL/capabilities.h 126 | --- a/pxr/imaging/hgiGL/capabilities.h (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 127 | +++ b/pxr/imaging/hgiGL/capabilities.h (date 1691662966564) 128 | @@ -52,6 +52,9 @@ 129 | HGIGL_API 130 | int GetShaderVersion() const override; 131 | 132 | + HGIGL_API 133 | + bool GetCoreProfile() const override; 134 | + 135 | private: 136 | void _LoadCapabilities(); 137 | 138 | @@ -60,6 +63,9 @@ 139 | 140 | // GLSL version 141 | int _glslVersion; // 400, 410, ... 142 | + 143 | + // Core Profile 144 | + bool _coreProfile; 145 | }; 146 | 147 | PXR_NAMESPACE_CLOSE_SCOPE 148 | Index: pxr/imaging/hgiInterop/opengl.h 149 | IDEA additional info: 150 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 151 | <+>UTF-8 152 | =================================================================== 153 | diff --git a/pxr/imaging/hgiInterop/opengl.h b/pxr/imaging/hgiInterop/opengl.h 154 | --- a/pxr/imaging/hgiInterop/opengl.h (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 155 | +++ b/pxr/imaging/hgiInterop/opengl.h (date 1691662966614) 156 | @@ -62,6 +62,7 @@ 157 | uint32_t _fsDepth; 158 | uint32_t _prgNoDepth; 159 | uint32_t _prgDepth; 160 | + uint32_t _vao; 161 | uint32_t _vertexBuffer; 162 | }; 163 | 164 | Index: pxr/imaging/hgiGL/blitCmds.cpp 165 | IDEA additional info: 166 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 167 | <+>UTF-8 168 | =================================================================== 169 | diff --git a/pxr/imaging/hgiGL/blitCmds.cpp b/pxr/imaging/hgiGL/blitCmds.cpp 170 | --- a/pxr/imaging/hgiGL/blitCmds.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 171 | +++ b/pxr/imaging/hgiGL/blitCmds.cpp (date 1691662966548) 172 | @@ -136,7 +136,7 @@ 173 | // Capture OpenGL state before executing the 'ops' and restore it when this 174 | // function ends. We do this defensively because parts of our pipeline may 175 | // not set and restore all relevant gl state. 176 | - HgiGL_ScopedStateHolder openglStateGuard; 177 | + HgiGL_ScopedStateHolder openglStateGuard(*hgi->GetCapabilities()); 178 | 179 | HgiGL* hgiGL = static_cast(hgi); 180 | HgiGLDevice* device = hgiGL->GetPrimaryDevice(); 181 | Index: pxr/imaging/hdSt/renderPassState.cpp 182 | IDEA additional info: 183 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 184 | <+>UTF-8 185 | =================================================================== 186 | diff --git a/pxr/imaging/hdSt/renderPassState.cpp b/pxr/imaging/hdSt/renderPassState.cpp 187 | --- a/pxr/imaging/hdSt/renderPassState.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 188 | +++ b/pxr/imaging/hdSt/renderPassState.cpp (date 1691662966532) 189 | @@ -837,7 +837,9 @@ 190 | // If not using GL_MULTISAMPLE, use GL_POINT_SMOOTH to render points as 191 | // circles instead of square. 192 | // XXX Switch points rendering to emit quad with FS that draws circle. 193 | - glEnable(GL_POINT_SMOOTH); 194 | + if (!hgiCapabilities.GetCoreProfile()) { 195 | + glEnable(GL_POINT_SMOOTH); 196 | + } 197 | } 198 | } 199 | 200 | @@ -881,7 +883,9 @@ 201 | } 202 | 203 | glEnable(GL_MULTISAMPLE); 204 | - glDisable(GL_POINT_SMOOTH); 205 | + if (!hgiCapabilities.GetCoreProfile()) { 206 | + glDisable(GL_POINT_SMOOTH); 207 | + } 208 | } 209 | 210 | void 211 | Index: pxr/imaging/hgiInterop/opengl.cpp 212 | IDEA additional info: 213 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 214 | <+>UTF-8 215 | =================================================================== 216 | diff --git a/pxr/imaging/hgiInterop/opengl.cpp b/pxr/imaging/hgiInterop/opengl.cpp 217 | --- a/pxr/imaging/hgiInterop/opengl.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 218 | +++ b/pxr/imaging/hgiInterop/opengl.cpp (date 1691662966606) 219 | @@ -111,12 +111,14 @@ 220 | , _prgNoDepth(0) 221 | , _prgDepth(0) 222 | , _vertexBuffer(0) 223 | + , _vao(0) 224 | { 225 | _vs = _CompileShader(_vertexFullscreen, GL_VERTEX_SHADER); 226 | _fsNoDepth = _CompileShader(_fragmentNoDepthFullscreen, GL_FRAGMENT_SHADER); 227 | _fsDepth = _CompileShader(_fragmentDepthFullscreen, GL_FRAGMENT_SHADER); 228 | _prgNoDepth = _LinkProgram(_vs, _fsNoDepth); 229 | _prgDepth = _LinkProgram(_vs, _fsDepth); 230 | + glCreateVertexArrays(1, &_vao); 231 | _vertexBuffer = _CreateVertexBuffer(); 232 | TF_VERIFY(glGetError() == GL_NO_ERROR); 233 | } 234 | @@ -129,6 +131,7 @@ 235 | glDeleteProgram(_prgNoDepth); 236 | glDeleteProgram(_prgDepth); 237 | glDeleteBuffers(1, &_vertexBuffer); 238 | + glDeleteVertexArrays(1, &_vao); 239 | TF_VERIFY(glGetError() == GL_NO_ERROR); 240 | } 241 | 242 | @@ -202,10 +205,13 @@ 243 | } 244 | 245 | // Get the current array buffer binding state 246 | + GLint restoreVao = 0; 247 | + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &restoreVao); 248 | GLint restoreArrayBuffer = 0; 249 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &restoreArrayBuffer); 250 | 251 | // Vertex attributes 252 | + glBindVertexArray(_vao); 253 | const GLint locPosition = glGetAttribLocation(prg, "position"); 254 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 255 | glVertexAttribPointer(locPosition, 4, GL_FLOAT, GL_FALSE, 256 | @@ -271,7 +277,8 @@ 257 | glDisableVertexAttribArray(locPosition); 258 | glDisableVertexAttribArray(locUv); 259 | glBindBuffer(GL_ARRAY_BUFFER, restoreArrayBuffer); 260 | - 261 | + glBindVertexArray(restoreVao); 262 | + 263 | if (!blendEnabled) { 264 | glDisable(GL_BLEND); 265 | } 266 | Index: pxr/imaging/hgi/capabilities.h 267 | IDEA additional info: 268 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 269 | <+>UTF-8 270 | =================================================================== 271 | diff --git a/pxr/imaging/hgi/capabilities.h b/pxr/imaging/hgi/capabilities.h 272 | --- a/pxr/imaging/hgi/capabilities.h (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 273 | +++ b/pxr/imaging/hgi/capabilities.h (date 1691662966539) 274 | @@ -52,6 +52,11 @@ 275 | HGI_API 276 | virtual int GetShaderVersion() const = 0; 277 | 278 | + HGI_API 279 | + virtual bool GetCoreProfile() const { 280 | + return false; 281 | + } 282 | + 283 | HGI_API 284 | size_t GetMaxUniformBlockSize() const { 285 | return _maxUniformBlockSize; 286 | Index: pxr/imaging/hdSt/indirectDrawBatch.h 287 | IDEA additional info: 288 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 289 | <+>UTF-8 290 | =================================================================== 291 | diff --git a/pxr/imaging/hdSt/indirectDrawBatch.h b/pxr/imaging/hdSt/indirectDrawBatch.h 292 | --- a/pxr/imaging/hdSt/indirectDrawBatch.h (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 293 | +++ b/pxr/imaging/hdSt/indirectDrawBatch.h (date 1691662966521) 294 | @@ -195,6 +195,8 @@ 295 | 296 | int _instanceCountOffset; 297 | int _cullInstanceCountOffset; 298 | + 299 | + uint32_t _vao; 300 | }; 301 | 302 | 303 | Index: pxr/imaging/hdSt/indirectDrawBatch.cpp 304 | IDEA additional info: 305 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 306 | <+>UTF-8 307 | =================================================================== 308 | diff --git a/pxr/imaging/hdSt/indirectDrawBatch.cpp b/pxr/imaging/hdSt/indirectDrawBatch.cpp 309 | --- a/pxr/imaging/hdSt/indirectDrawBatch.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 310 | +++ b/pxr/imaging/hdSt/indirectDrawBatch.cpp (date 1691662966511) 311 | @@ -109,11 +109,17 @@ 312 | , _allowGpuFrustumCulling(allowGpuFrustumCulling) 313 | , _instanceCountOffset(0) 314 | , _cullInstanceCountOffset(0) 315 | + , _vao(0) 316 | { 317 | _Init(drawItemInstance); 318 | } 319 | 320 | -HdSt_IndirectDrawBatch::~HdSt_IndirectDrawBatch() = default; 321 | +HdSt_IndirectDrawBatch::~HdSt_IndirectDrawBatch() 322 | +{ 323 | + if (_vao) { 324 | + glDeleteVertexArrays(1, &_vao); 325 | + } 326 | +} 327 | 328 | /*virtual*/ 329 | void 330 | @@ -1146,6 +1152,14 @@ 331 | state.instancePrimvarBars); 332 | } 333 | 334 | + // OpenGL core profile requries a VAO for binding buffers. 335 | + if (capabilities->GetCoreProfile()) { 336 | + if (!_vao) { 337 | + glCreateVertexArrays(1, &_vao); 338 | + } 339 | + glBindVertexArray(_vao); 340 | + } 341 | + 342 | state.BindResourcesForDrawing(renderPassState, *capabilities); 343 | 344 | HdSt_GeometricShaderSharedPtr geometricShader = state.geometricShader; 345 | @@ -1374,6 +1388,16 @@ 346 | 347 | Hgi * hgi = resourceRegistry->GetHgi(); 348 | 349 | + HgiCapabilities const *capabilities = hgi->GetCapabilities(); 350 | + 351 | + // OpenGL core profile requries a VAO for binding buffers. 352 | + if (capabilities->GetCoreProfile()) { 353 | + if (!_vao) { 354 | + glCreateVertexArrays(1, &_vao); 355 | + } 356 | + glBindVertexArray(_vao); 357 | + } 358 | + 359 | HgiGraphicsPipelineSharedPtr const & pso = 360 | _GetCullPipeline(resourceRegistry, 361 | state.glslProgram, 362 | Index: pxr/imaging/hgiGL/graphicsPipeline.cpp 363 | IDEA additional info: 364 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 365 | <+>UTF-8 366 | =================================================================== 367 | diff --git a/pxr/imaging/hgiGL/graphicsPipeline.cpp b/pxr/imaging/hgiGL/graphicsPipeline.cpp 368 | --- a/pxr/imaging/hgiGL/graphicsPipeline.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 369 | +++ b/pxr/imaging/hgiGL/graphicsPipeline.cpp (date 1691662966578) 370 | @@ -42,7 +42,12 @@ 371 | { 372 | } 373 | 374 | -HgiGLGraphicsPipeline::~HgiGLGraphicsPipeline() = default; 375 | +HgiGLGraphicsPipeline::~HgiGLGraphicsPipeline() 376 | +{ 377 | + if (_vao) { 378 | + glDeleteVertexArrays(1, &_vao); 379 | + } 380 | +} 381 | 382 | void 383 | HgiGLGraphicsPipeline::BindPipeline() 384 | @@ -50,6 +55,7 @@ 385 | if (_vao) { 386 | glBindVertexArray(0); 387 | glDeleteVertexArrays(1, &_vao); 388 | + _vao = 0; 389 | } 390 | 391 | if (!_descriptor.vertexBuffers.empty()) { 392 | @@ -108,6 +114,7 @@ 393 | glBindVertexArray(_vao); 394 | } 395 | 396 | + const bool coreProfile = _hgi->GetCapabilities()->GetCoreProfile(); 397 | // 398 | // Depth Stencil State 399 | // 400 | @@ -172,7 +179,9 @@ 401 | // If not using GL_MULTISAMPLE, use GL_POINT_SMOOTH to render points as 402 | // circles instead of square. 403 | // XXX Switch points rendering to emit quad with FS that draws circle. 404 | - glEnable(GL_POINT_SMOOTH); 405 | + if (!coreProfile) { 406 | + glEnable(GL_POINT_SMOOTH); 407 | + } 408 | } 409 | if (_descriptor.multiSampleState.alphaToCoverageEnable) { 410 | glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); 411 | @@ -207,7 +216,7 @@ 412 | glFrontFace(GL_CCW); 413 | } 414 | 415 | - if (_descriptor.rasterizationState.lineWidth != 1.0f) { 416 | + if (!coreProfile && _descriptor.rasterizationState.lineWidth != 1.0f) { 417 | glLineWidth(_descriptor.rasterizationState.lineWidth); 418 | } 419 | 420 | Index: pxr/imaging/hgiGL/scopedStateHolder.h 421 | IDEA additional info: 422 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 423 | <+>UTF-8 424 | =================================================================== 425 | diff --git a/pxr/imaging/hgiGL/scopedStateHolder.h b/pxr/imaging/hgiGL/scopedStateHolder.h 426 | --- a/pxr/imaging/hgiGL/scopedStateHolder.h (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 427 | +++ b/pxr/imaging/hgiGL/scopedStateHolder.h (date 1691662966599) 428 | @@ -32,6 +32,7 @@ 429 | 430 | PXR_NAMESPACE_OPEN_SCOPE 431 | 432 | +class HgiCapabilities; 433 | 434 | /// \class HgiGLScopedStateHolder 435 | /// 436 | @@ -50,7 +51,7 @@ 437 | { 438 | public: 439 | HGIGL_API 440 | - HgiGL_ScopedStateHolder(); 441 | + HgiGL_ScopedStateHolder(HgiCapabilities const& capabilities); 442 | 443 | HGIGL_API 444 | ~HgiGL_ScopedStateHolder(); 445 | @@ -59,6 +60,8 @@ 446 | HgiGL_ScopedStateHolder& operator=(const HgiGL_ScopedStateHolder&) = delete; 447 | HgiGL_ScopedStateHolder(const HgiGL_ScopedStateHolder&) = delete; 448 | 449 | + bool _coreProfile; 450 | + 451 | int32_t _restoreRenderBuffer; 452 | int32_t _restoreVao; 453 | 454 | Index: pxr/imaging/hgiGL/graphicsCmds.cpp 455 | IDEA additional info: 456 | Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP 457 | <+>UTF-8 458 | =================================================================== 459 | diff --git a/pxr/imaging/hgiGL/graphicsCmds.cpp b/pxr/imaging/hgiGL/graphicsCmds.cpp 460 | --- a/pxr/imaging/hgiGL/graphicsCmds.cpp (revision b53573ea2a6b29bc4a6b129f604bbb342c35df5c) 461 | +++ b/pxr/imaging/hgiGL/graphicsCmds.cpp (date 1691662966570) 462 | @@ -249,7 +249,7 @@ 463 | // Capture OpenGL state before executing the 'ops' and restore it when this 464 | // function ends. We do this defensively because parts of our pipeline may 465 | // not set and restore all relevant gl state. 466 | - HgiGL_ScopedStateHolder openglStateGuard; 467 | + HgiGL_ScopedStateHolder openglStateGuard(*hgi->GetCapabilities()); 468 | 469 | // Resolve multisample textures 470 | HgiGL* hgiGL = static_cast(hgi); 471 | --------------------------------------------------------------------------------