diff --git a/hydra_storm/__init__.py b/hydra_storm/__init__.py new file mode 100644 index 000000000..811296f69 --- /dev/null +++ b/hydra_storm/__init__.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2011-2022 Blender Foundation + +# + + +bl_info = { + "name": "Hydra Storm render engine", + "author": "AMD", + "version": (1, 0, 0), + "blender": (4, 0, 0), + "description": "USD's high performance rasterizing renderer", + "tracker_url": "", + "doc_url": "", + "community": "", + "downloads": "", + "main_web": "", + "support": 'OFFICIAL', + "category": "Render" +} + + +from . import engine, properties, ui + + +def register(): + engine.register() + properties.register() + ui.register() + + +def unregister(): + ui.unregister() + properties.unregister() + engine.unregister() diff --git a/hydra_storm/engine.py b/hydra_storm/engine.py new file mode 100644 index 000000000..8037746ff --- /dev/null +++ b/hydra_storm/engine.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2011-2022 Blender Foundation + +# + +import bpy + +class StormHydraRenderEngine(bpy.types.HydraRenderEngine): + bl_idname = 'HYDRA_STORM' + bl_label = "Hydra Storm" + bl_info = "USD's high performance rasterizing renderer" + + bl_use_preview = True + bl_use_gpu_context = True + + bl_delegate_id = 'HdStormRendererPlugin' + + def get_render_settings(self, engine_type): + settings = bpy.context.scene.hydra_storm.viewport if engine_type == 'VIEWPORT' else \ + bpy.context.scene.hydra_storm.final + result = { + 'enableTinyPrimCulling': settings.enable_tiny_prim_culling, + 'volumeRaymarchingStepSize': settings.volume_raymarching_step_size, + 'volumeRaymarchingStepSizeLighting': settings.volume_raymarching_step_size_lighting, + 'volumeMaxTextureMemoryPerField': settings.volume_max_texture_memory_per_field, + 'maxLights': settings.max_lights, + } + + if engine_type != 'VIEWPORT': + result |= { + 'aovToken:Combined': "color", + 'aovToken:Depth': "depth", + } + + return result + + def update_render_passes(self, scene, render_layer): + if render_layer.use_pass_z: + self.register_pass(scene, render_layer, 'Depth', 1, 'Z', 'VALUE') + + +register, unregister = bpy.utils.register_classes_factory(( + StormHydraRenderEngine, +)) diff --git a/hydra_storm/properties.py b/hydra_storm/properties.py new file mode 100644 index 000000000..ad29a5c40 --- /dev/null +++ b/hydra_storm/properties.py @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2011-2022 Blender Foundation + +# + +import bpy + + +class Properties(bpy.types.PropertyGroup): + type = None + + @classmethod + def register(cls): + cls.type.hydra_storm = bpy.props.PointerProperty( + name="Hydra Storm", + description="Hydra Storm properties", + type=cls, + ) + + @classmethod + def unregister(cls): + del cls.type.hydra_storm + + +class RenderProperties(bpy.types.PropertyGroup): + enable_tiny_prim_culling: bpy.props.BoolProperty( + name="Tiny Prim Culling", + description="Enable Tiny Prim Culling", + default=False, + ) + volume_raymarching_step_size: bpy.props.FloatProperty( + name="Volume Raymarching Step Size", + description="Step size when raymarching volume", + default=1.0, + ) + volume_raymarching_step_size_lighting: bpy.props.FloatProperty( + name="Volume Raymarching Step Size Lighting", + description="Step size when raymarching volume for lighting computation", + default=10.0, + ) + volume_max_texture_memory_per_field: bpy.props.FloatProperty( + name="Max Texture Memory Per Field", + description="Maximum memory for a volume field texture in Mb (unless overridden by field prim)", + default=128.0, + ) + max_lights: bpy.props.IntProperty( + name="Max Lights", + description="Limit maximum number of lights", + default=16, min=0, max=16, + ) + + +class SceneProperties(Properties): + type = bpy.types.Scene + + final: bpy.props.PointerProperty(type=RenderProperties) + viewport: bpy.props.PointerProperty(type=RenderProperties) + + +register, unregister = bpy.utils.register_classes_factory(( + RenderProperties, + SceneProperties, +)) diff --git a/hydra_storm/ui.py b/hydra_storm/ui.py new file mode 100644 index 000000000..44d220746 --- /dev/null +++ b/hydra_storm/ui.py @@ -0,0 +1,243 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2011-2022 Blender Foundation + +# + +import bpy + +from .engine import StormHydraRenderEngine + + +class Panel(bpy.types.Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = 'render' + COMPAT_ENGINES = {StormHydraRenderEngine.bl_idname} + + @classmethod + def poll(cls, context): + return context.engine in cls.COMPAT_ENGINES + + +# +# Final render settings +# +class STORM_HYDRA_RENDER_PT_final(Panel): + """Final render delegate and settings""" + bl_idname = 'STORM_HYDRA_RENDER_PT_final' + bl_label = "Final Render Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.scene.hydra_storm.final + layout.prop(settings, 'enable_tiny_prim_culling') + layout.prop(settings, 'max_lights') + + +class STORM_HYDRA_RENDER_PT_volume_final(bpy.types.Panel): + bl_parent_id = STORM_HYDRA_RENDER_PT_final.bl_idname + bl_label = "Volume Raymarching" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.scene.hydra_storm.final + + col = layout.column(align=True) + col.prop(settings, "volume_raymarching_step_size", text="Step Size") + col.prop(settings, "volume_raymarching_step_size_lighting", text="Step Size Lightning") + col.prop(settings, "volume_max_texture_memory_per_field") + + +# +# Viewport render settings +# +class STORM_HYDRA_RENDER_PT_viewport(Panel): + """Viewport render delegate and settings""" + bl_idname = 'STORM_HYDRA_RENDER_PT_viewport' + bl_label = "Viewport Render Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.scene.hydra_storm.viewport + layout.prop(settings, 'enable_tiny_prim_culling') + layout.prop(settings, 'max_lights') + + +class STORM_HYDRA_RENDER_PT_volume_viewport(bpy.types.Panel): + bl_parent_id = STORM_HYDRA_RENDER_PT_viewport.bl_idname + bl_label = "Volume Raymarching" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.scene.hydra_storm.viewport + + col = layout.column(align=True) + col.prop(settings, "volume_raymarching_step_size", text="Step Size") + col.prop(settings, "volume_raymarching_step_size_lighting", text="Step Size Lightning") + col.prop(settings, "volume_max_texture_memory_per_field") + + +class STORM_HYDRA_LIGHT_PT_light(Panel): + """Physical light sources""" + bl_label = "Light" + bl_context = 'data' + + @classmethod + def poll(cls, context): + return super().poll(context) and context.light + + def draw(self, context): + layout = self.layout + + light = context.light + + layout.prop(light, "type", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = False + + main_col = layout.column() + + main_col.prop(light, "color") + main_col.prop(light, "energy") + main_col.separator() + + if light.type == 'POINT': + row = main_col.row(align=True) + row.prop(light, "shadow_soft_size", text="Radius") + + elif light.type == 'SPOT': + col = main_col.column(align=True) + col.prop(light, 'spot_size', slider=True) + col.prop(light, 'spot_blend', slider=True) + + main_col.prop(light, 'show_cone') + + elif light.type == 'SUN': + main_col.prop(light, "angle") + + elif light.type == 'AREA': + main_col.prop(light, "shape", text="Shape") + sub = main_col.column(align=True) + + if light.shape in {'SQUARE', 'DISK'}: + sub.prop(light, "size") + elif light.shape in {'RECTANGLE', 'ELLIPSE'}: + sub.prop(light, "size", text="Size X") + sub.prop(light, "size_y", text="Y") + + else: + main_col.prop(light, 'size') + + +class STORM_HYDRA_RENDER_PT_film(Panel): + bl_label = "Film" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + layout.prop(context.scene.render, "film_transparent", text="Transparent Background") + + +class STORM_HYDRA_RENDER_PT_passes(Panel): + bl_label = "Passes" + bl_context = "view_layer" + + def draw(self, context): + pass + + +class STORM_HYDRA_RENDER_PT_passes_data(Panel): + bl_label = "Data" + bl_context = "view_layer" + bl_parent_id = "STORM_HYDRA_RENDER_PT_passes" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + + col = layout.column(heading="Include", align=True) + col.prop(view_layer, "use_pass_z") + + +register_classes, unregister_classes = bpy.utils.register_classes_factory(( + STORM_HYDRA_RENDER_PT_final, + STORM_HYDRA_RENDER_PT_volume_final, + STORM_HYDRA_RENDER_PT_viewport, + STORM_HYDRA_RENDER_PT_volume_viewport, + STORM_HYDRA_RENDER_PT_film, + STORM_HYDRA_LIGHT_PT_light, + STORM_HYDRA_RENDER_PT_passes, + STORM_HYDRA_RENDER_PT_passes_data, +)) + + +def get_panels(): + # Follow the Cycles model of excluding panels we don't want. + exclude_panels = { + 'RENDER_PT_stamp', + 'DATA_PT_light', + 'DATA_PT_spot', + 'NODE_DATA_PT_light', + 'DATA_PT_falloff_curve', + 'RENDER_PT_post_processing', + 'RENDER_PT_simplify', + 'SCENE_PT_audio', + 'RENDER_PT_freestyle' + } + include_eevee_panels = { + 'MATERIAL_PT_preview', + 'EEVEE_MATERIAL_PT_context_material', + 'EEVEE_MATERIAL_PT_surface', + 'EEVEE_MATERIAL_PT_volume', + 'EEVEE_MATERIAL_PT_settings', + 'EEVEE_WORLD_PT_surface', + } + + for panel_cls in bpy.types.Panel.__subclasses__(): + if hasattr(panel_cls, 'COMPAT_ENGINES') and ( + ('BLENDER_RENDER' in panel_cls.COMPAT_ENGINES and panel_cls.__name__ not in exclude_panels) or + ('BLENDER_EEVEE' in panel_cls.COMPAT_ENGINES and panel_cls.__name__ in include_eevee_panels) + ): + yield panel_cls + + +def register(): + register_classes() + + for panel_cls in get_panels(): + panel_cls.COMPAT_ENGINES.add(StormHydraRenderEngine.bl_idname) + + +def unregister(): + unregister_classes() + + for panel_cls in get_panels(): + if StormHydraRenderEngine.bl_idname in panel_cls.COMPAT_ENGINES: + panel_cls.COMPAT_ENGINES.remove(StormHydraRenderEngine.bl_idname)