diff --git a/.github/workflows/BlenderMalt.yml b/.github/workflows/BlenderMalt.yml index 6e815e81..5d0fde79 100644 --- a/.github/workflows/BlenderMalt.yml +++ b/.github/workflows/BlenderMalt.yml @@ -22,13 +22,24 @@ jobs: - uses: ncipollo/release-action@v1 id: create_release + if: ${{ github.ref_type == 'branch' }} with: token: ${{ secrets.GITHUB_TOKEN }} allowUpdates: true tag: ${{ env.BRANCH_NAME }}-latest + commit: ${{ github.sha }} prerelease: ${{ env.BRANCH_NAME != 'Release' }} - bodyFile: ".github/download.md" - if: ${{ github.ref_type == 'branch' }} + body: | + ### Sponsored by + ## Creative Shrimp + ## オリトイツキ - Orito Itsuki + --- + [**BlenderMalt-Windows.zip**](https://github.com/${{github.repository}}/releases/download/${{env.BRANCH_NAME}}-latest/BlenderMalt-Windows.zip) + [**BlenderMalt-Linux.zip**](https://github.com/${{github.repository}}/releases/download/${{env.BRANCH_NAME}}-latest/BlenderMalt-Linux.zip) + + *(Requires Blender 3.2)* + --- + - name: Rollback Tagged Release uses: author/action-rollback@stable @@ -41,12 +52,23 @@ jobs: - uses: ncipollo/release-action@v1 id: create_tagged_release + if: ${{ github.ref_type == 'tag' }} with: token: ${{ secrets.GITHUB_TOKEN }} + commit: ${{ github.sha }} allowUpdates: true prerelease: ${{ env.BRANCH_NAME != 'Release' }} - bodyFile: ".github/download.md" - if: ${{ github.ref_type == 'tag' }} + body: | + ### Sponsored by + ## Creative Shrimp + ## オリトイツキ - Orito Itsuki + --- + [**BlenderMalt-Windows.zip**](https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/BlenderMalt-Windows.zip) + [**BlenderMalt-Linux.zip**](https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/BlenderMalt-Linux.zip) + + *(Requires Blender 3.2)* + --- + outputs: upload_url: ${{ steps.create_release.outputs.upload_url }} @@ -60,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-latest, ubuntu-18.04] + os: [windows-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/BlenderMalt/MaltNodes/MaltNodeTree.py b/BlenderMalt/MaltNodes/MaltNodeTree.py index a5e810e2..5bd0833c 100644 --- a/BlenderMalt/MaltNodes/MaltNodeTree.py +++ b/BlenderMalt/MaltNodes/MaltNodeTree.py @@ -151,12 +151,15 @@ def get_generated_source(self, force_update=False): output_nodes.append(node) linked_nodes.append(node) - def add_node_inputs(node, list): + def add_node_inputs(node, list, io_type): for input in node.inputs: if input.get_linked(): new_node = input.get_linked().node + if new_node.bl_idname == 'MaltIONode' and new_node.io_type != io_type: + input.links[0].is_muted = True + continue if new_node not in list: - add_node_inputs(new_node, list) + add_node_inputs(new_node, list, io_type) list.append(new_node) if new_node not in linked_nodes: linked_nodes.append(new_node) @@ -164,7 +167,7 @@ def add_node_inputs(node, list): transpiler = self.get_transpiler() def get_source(output): nodes = [] - add_node_inputs(output, nodes) + add_node_inputs(output, nodes, output.io_type) code = '' for node in nodes: if isinstance(node, MaltNode): @@ -219,11 +222,12 @@ def update_ext(self, force_track_shader_changes=True): try: for link in self.links: try: - if (link.from_socket.array_size != link.to_socket.array_size or - (link.from_socket.data_type != link.to_socket.data_type and - self.cast(link.from_socket.data_type, link.to_socket.data_type) is None)): - #TODO: handle reroute nodes - self.links.remove(link) + b = link.to_socket + a = b.get_linked() + if (a.array_size != b.array_size or + (a.data_type != b.data_type and + self.cast(a.data_type, b.data_type) is None)): + link.is_muted = True except: pass @@ -557,7 +561,7 @@ def node_header_ui(self, context): if context.space_data.tree_type != 'MaltTree' or node_tree is None: return def duplicate(): - node_tree = node_tree.copy() + context.space_data.node_tree = node_tree.copy() self.layout.operator('wm.malt_callback', text='', icon='DUPLICATE').callback.set(duplicate, 'Duplicate') def recompile(): node_tree.update() diff --git a/BlenderMalt/MaltNodes/MaltSocket.py b/BlenderMalt/MaltNodes/MaltSocket.py index 6a81c812..be891686 100644 --- a/BlenderMalt/MaltNodes/MaltSocket.py +++ b/BlenderMalt/MaltNodes/MaltSocket.py @@ -26,8 +26,9 @@ def on_type_update(self, context): default_initialization: bpy.props.StringProperty(default='', options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) - show_in_material_panel: bpy.props.BoolProperty(default=True, - options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) + # Declared below, in register() + #show_in_material_panel: bpy.props.BoolProperty(default=True, + # options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) active: bpy.props.BoolProperty(default=True, options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) @@ -80,6 +81,8 @@ def get_linked_internal(socket): return None else: link = socket.links[0] + if link.is_muted == True: + return None linked = link.to_socket if socket.is_output else link.from_socket if isinstance(linked.node, bpy.types.NodeReroute): sockets = linked.node.inputs if linked.is_output else linked.node.outputs @@ -144,6 +147,11 @@ def draw_color(self, context, node): def register(): for _class in classes: bpy.utils.register_class(_class) + + #Declare MaltSocket.show_in_material_panel + preferences = bpy.context.preferences.addons['BlenderMalt'].preferences + preferences.update_show_sockets(None) + def unregister(): for _class in reversed(classes): bpy.utils.unregister_class(_class) diff --git a/BlenderMalt/MaltPipeline.py b/BlenderMalt/MaltPipeline.py index b5bdf8d8..2bd52bb1 100644 --- a/BlenderMalt/MaltPipeline.py +++ b/BlenderMalt/MaltPipeline.py @@ -112,7 +112,7 @@ def update_pipeline_settings(self, context): set=malt_path_setter('plugins_dir'), get=malt_path_getter('plugins_dir'), options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) - viewport_bit_depth : bpy.props.EnumProperty(items=[('8', '8', ''),('32', '32', '')], + viewport_bit_depth : bpy.props.EnumProperty(items=[('8', '8', ''),('16', '16', ''),('32', '32', '')], name="Bit Depth (Viewport)", update=update_pipeline_settings, options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) graph_types : bpy.props.CollectionProperty(type=bpy.types.PropertyGroup, diff --git a/BlenderMalt/MaltRenderEngine.py b/BlenderMalt/MaltRenderEngine.py index 35e3cd5e..8329a102 100644 --- a/BlenderMalt/MaltRenderEngine.py +++ b/BlenderMalt/MaltRenderEngine.py @@ -1,4 +1,5 @@ import ctypes, time, platform +import xxhash import bpy from mathutils import Vector, Matrix, Quaternion from Malt import Scene @@ -187,7 +188,7 @@ def add_object(obj, matrix, id): for obj in depsgraph.objects: if is_f12 or obj.visible_in_viewport_get(context.space_data): - id = abs(hash(obj.name_full)) % (2**16) + id = xxhash.xxh3_64_intdigest(obj.name_full.encode()) % (2**16) add_object(obj, obj.matrix_world, id) for instance in depsgraph.object_instances: @@ -354,6 +355,9 @@ def view_draw(self, context, depsgraph): texture_format = GL.GL_RGBA8 if GL.glGetInternalformativ(GL.GL_TEXTURE_2D, texture_format, GL.GL_READ_PIXELS, 1) != GL.GL_ZERO: data_format = GL.glGetInternalformativ(GL.GL_TEXTURE_2D, texture_format, GL.GL_TEXTURE_IMAGE_TYPE, 1) + elif self.bridge.viewport_bit_depth == 16: + data_format = GL.GL_HALF_FLOAT + texture_format = GL.GL_RGBA16F render_texture = Texture(resolution, texture_format, data_format, pixels.buffer(), mag_filter=mag_filter) diff --git a/BlenderMalt/MaltTextures.py b/BlenderMalt/MaltTextures.py index abac8fa1..cc1e0a4a 100644 --- a/BlenderMalt/MaltTextures.py +++ b/BlenderMalt/MaltTextures.py @@ -14,7 +14,7 @@ def __load_texture(texture): w,h = texture.size channels = int(texture.channels) size = w*h*channels - sRGB = texture.colorspace_settings.name == 'sRGB' and texture.use_generated_float == False + sRGB = texture.colorspace_settings.name == 'sRGB' and texture.is_float == False if size == 0: return True diff --git a/BlenderMalt/__init__.py b/BlenderMalt/__init__.py index cf959a7d..64a06d04 100644 --- a/BlenderMalt/__init__.py +++ b/BlenderMalt/__init__.py @@ -2,7 +2,7 @@ "name": "BlenderMalt", "description" : "Extensible Python Render Engine", "author" : "Miguel Pozo", - "version": (1,0,0,'beta.2','Release'), + "version": (1,0,0,'beta.3','Release'), "blender" : (3, 2, 0), "doc_url": "https://malt3d.com", "tracker_url": "https://github.com/bnpr/Malt/issues/new/choose", @@ -34,7 +34,8 @@ class Preferences(bpy.types.AddonPreferences): # this must match the addon name bl_idname = __package__ - setup_vs_code : bpy.props.BoolProperty(name="Auto setup VSCode", default=True, description="Setups a VSCode project on your .blend file folder") + setup_vs_code : bpy.props.BoolProperty(name="Auto setup VSCode", default=True, + description="Setups a VSCode project on your .blend file folder") renderdoc_path : bpy.props.StringProperty(name="RenderDoc Path", subtype='FILE_PATH', set=malt_path_setter('renderdoc_path'), get=malt_path_getter('renderdoc_path')) @@ -47,11 +48,20 @@ class Preferences(bpy.types.AddonPreferences): render_fps_cap : bpy.props.IntProperty(name="Max Viewport Render Framerate", default=30) + def update_show_sockets(self, context): + from BlenderMalt.MaltNodes.MaltSocket import MaltSocket + MaltSocket.show_in_material_panel = bpy.props.BoolProperty(default=self.show_sockets, + options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) + + show_sockets : bpy.props.BoolProperty(name="Show sockets in Material Panel", default=True, + update=update_show_sockets, description="Show node socket properties in the Material Panel by default") + def update_debug_mode(self, context): if context.scene.render.engine == 'MALT': context.scene.world.malt.update_pipeline(context) - debug_mode : bpy.props.BoolProperty(name="Debug Mode", default=False, update=update_debug_mode, description="Developers only. Do not touch !!!") + debug_mode : bpy.props.BoolProperty(name="Debug Mode", default=False, update=update_debug_mode, + description="Developers only. Do not touch !!!") def draw(self, context): layout = self.layout @@ -68,9 +78,10 @@ def draw(self, context): row.operator('wm.path_open', text="Open Session Log") layout.prop(self, "plugins_dir") + layout.prop(self, "show_sockets") + layout.prop(self, "render_fps_cap") layout.prop(self, "setup_vs_code") layout.prop(self, "renderdoc_path") - layout.prop(self, "render_fps_cap") layout.label(text='Developer Settings :') layout.prop(self, "debug_mode") layout.prop(self, "docs_path") diff --git a/Bridge/Client_API.py b/Bridge/Client_API.py index ddaddd33..135d926b 100644 --- a/Bridge/Client_API.py +++ b/Bridge/Client_API.py @@ -259,8 +259,11 @@ def render(self, viewport_id, resolution, scene, scene_update, renderdoc_capture from itertools import chain for key, texture_format in chain(self.render_outputs.items(), AOVs.items()): buffer_type = ctypes.c_float - if viewport_id != 0 and self.viewport_bit_depth == 8: - buffer_type = ctypes.c_byte + if viewport_id != 0: + if self.viewport_bit_depth == 8: + buffer_type = ctypes.c_byte + elif self.viewport_bit_depth == 16: + buffer_type = ctypes.c_ushort w,h = resolution self.render_buffers[viewport_id][key] = self.get_shared_buffer(buffer_type, w*h*4) if viewport_id != 0: #viewport render diff --git a/Bridge/Server.py b/Bridge/Server.py index 4c99d848..f3a49db6 100644 --- a/Bridge/Server.py +++ b/Bridge/Server.py @@ -169,6 +169,12 @@ def setup(self, new_buffers, resolution, scene, scene_update, renderdoc_capture) self.final_texture = Texture(resolution, GL_RGBA8, GL_UNSIGNED_BYTE, pixel_format=GL_RGBA) self.final_texture.channel_size = 1 self.final_target = RenderTarget([self.final_texture]) + elif self.bit_depth == 16: + self.final_texture = Texture(resolution, GL_RGBA16F) + self.final_target = RenderTarget([self.final_texture]) + elif self.bit_depth == 32: + self.final_texture = Texture(resolution, GL_RGBA32F) + self.final_target = RenderTarget([self.final_texture]) if new_buffers: self.buffers = new_buffers @@ -202,7 +208,7 @@ def render(self): if self.needs_more_samples: result = self.pipeline.render(self.resolution, self.scene, self.is_final_render, self.is_new_frame) - if self.final_texture: + if self.final_texture.internal_format != result['COLOR'].internal_format: self.pipeline.copy_textures(self.final_target, [result['COLOR']]) result = { 'COLOR' : self.final_texture } self.is_new_frame = False diff --git a/Malt/GL/GL.py b/Malt/GL/GL.py index 4708ee52..807f1960 100644 --- a/Malt/GL/GL.py +++ b/Malt/GL/GL.py @@ -11,8 +11,17 @@ #For some reason PyOpenGL doesnt support the most common depth/stencil buffer by default ??? #https://sourceforge.net/p/pyopengl/bugs/223/ from OpenGL import images - images.TYPE_TO_ARRAYTYPE[ GL_UNSIGNED_INT_24_8 ] = GL_UNSIGNED_INT - images.TIGHT_PACK_FORMATS[ GL_UNSIGNED_INT_24_8 ] = 4 + images.TYPE_TO_ARRAYTYPE[GL_UNSIGNED_INT_24_8] = GL_UNSIGNED_INT + images.TIGHT_PACK_FORMATS[GL_UNSIGNED_INT_24_8] = 4 + images.TYPE_TO_ARRAYTYPE[GL_HALF_FLOAT] = GL_HALF_FLOAT + from OpenGL import arrays + if arrays.ADT: + arrays.GL_CONSTANT_TO_ARRAY_TYPE[GL_HALF_FLOAT] = arrays.ADT(GL_HALF_FLOAT, GLhalfARB) + else: + class GLhalfFloatArray(ArrayDatatype, ctypes.POINTER(GLhalfARB)): + baseType = GLhalfARB + typeConstant = GL_HALF_FLOAT + arrays.GL_CONSTANT_TO_ARRAY_TYPE[GL_HALF_FLOAT] = GLhalfFloatArray NULL = None GL_ENUMS = {} @@ -56,6 +65,8 @@ def gl_buffer(type, size, data=None): GL_UNSIGNED_SHORT : GLushort, GL_INT : GLint, GL_UNSIGNED_INT : GLuint, + #GL_HALF_FLOAT : GLhalfARB, + GL_HALF_FLOAT : GLfloat, GL_FLOAT : GLfloat, GL_DOUBLE : GLdouble, GL_BOOL : GLboolean, diff --git a/Malt/GL/RenderTarget.py b/Malt/GL/RenderTarget.py index 9c8f5672..a0d2abe5 100644 --- a/Malt/GL/RenderTarget.py +++ b/Malt/GL/RenderTarget.py @@ -59,6 +59,7 @@ def clear(self, colors=[], depth=None, stencil=None): GL_INT : glClearBufferiv, GL_UNSIGNED_INT : glClearBufferuiv, GL_FLOAT : glClearBufferfv, + GL_HALF_FLOAT : glClearBufferfv, GL_UNSIGNED_BYTE : glClearBufferfv, } target = self.targets[i] diff --git a/Malt/GL/Shader.py b/Malt/GL/Shader.py index a8241639..d3122aa4 100644 --- a/Malt/GL/Shader.py +++ b/Malt/GL/Shader.py @@ -526,9 +526,12 @@ def glsl_reflection(code, root_paths=[]): except: pass - def fix_paths(dic): + def handle_paths(dic): for e in dic.values(): path = e['file'] + if '.internal.' in path or '__internal__' in path: + if 'internal' not in e['meta'].keys(): + e['meta']['internal'] = True path = os.path.normpath(path) for root_path in root_paths: try: @@ -538,8 +541,8 @@ def fix_paths(dic): except: pass e['file'] = path.replace('\\','/') - fix_paths(reflection['structs']) - fix_paths(reflection['functions']) + handle_paths(reflection['structs']) + handle_paths(reflection['functions']) functions = {} for key, function in reflection['functions'].items(): diff --git a/Malt/GL/Texture.py b/Malt/GL/Texture.py index c8dd8bd9..86eef3bd 100644 --- a/Malt/GL/Texture.py +++ b/Malt/GL/Texture.py @@ -172,7 +172,8 @@ def __del__(self): def internal_format_to_data_format(internal_format): name = GL_ENUMS[internal_format] table = { - 'F' : GL_FLOAT, + '32F' : GL_FLOAT, + '16F' : GL_HALF_FLOAT, 'UI' : GL_UNSIGNED_INT, 'I' : GL_INT, } @@ -197,6 +198,7 @@ def internal_format_to_sampler_type(internal_format): table = { GL_UNSIGNED_BYTE : 'sampler2D', GL_FLOAT : 'sampler2D', + GL_HALF_FLOAT : 'sampler2D', GL_INT : 'isampler2D', GL_UNSIGNED_INT : 'usampler2D' } diff --git a/Malt/Nodes/Unpack8bitTextures.py b/Malt/Nodes/Unpack8bitTextures.py deleted file mode 100644 index 3887e428..00000000 --- a/Malt/Nodes/Unpack8bitTextures.py +++ /dev/null @@ -1,64 +0,0 @@ -from Malt.PipelineNode import PipelineNode -from Malt.PipelineParameters import Parameter, Type, MaterialParameter - -_UNPACK_SRC = """ -#include "Passes/Unpack8bitTextures.glsl" -""" -_UNPACK_SHADER = None - -class Unpack8bitTextures(PipelineNode): - - """ - Unpacks up to 4 textures packed into a single one using the *pack_8bit* shader function. - *(Useful when a shader needs to output more than 8 textures)* - """ - - def __init__(self, pipeline): - self.pipeline = pipeline - self.resolution = None - self.texture_targets = [None]*4 - self.render_target = None - - @classmethod - def reflect_inputs(cls): - return { - 'Packed Texture' : Parameter('usampler2D', Type.OTHER) - } - - @classmethod - def reflect_outputs(cls): - return { - 'A' : Parameter('', Type.TEXTURE), - 'B' : Parameter('', Type.TEXTURE), - 'C' : Parameter('', Type.TEXTURE), - 'D' : Parameter('', Type.TEXTURE), - } - - def execute(self, parameters): - from Malt.GL import GL - from Malt.GL.Texture import Texture - from Malt.GL.RenderTarget import RenderTarget - - if self.pipeline.resolution != self.resolution: - for i in range(4): - #TODO: Doesn't work with GL_RGBA? - self.texture_targets[i] = Texture(self.pipeline.resolution, GL.GL_RGBA16F) - self.render_target = RenderTarget(self.texture_targets) - self.resolution = self.pipeline.resolution - - self.render_target.clear([(0,0,0,0)]*4) - - global _UNPACK_SHADER - if _UNPACK_SHADER is None: - _UNPACK_SHADER = self.pipeline.compile_shader_from_source(_UNPACK_SRC) - - _UNPACK_SHADER.textures['IN_PACKED'] = parameters['Packed Texture'] - self.pipeline.draw_screen_pass(_UNPACK_SHADER, self.render_target, blend = False) - - parameters['A'] = self.texture_targets[0] - parameters['B'] = self.texture_targets[1] - parameters['C'] = self.texture_targets[2] - parameters['D'] = self.texture_targets[3] - - -NODE = Unpack8bitTextures diff --git a/Malt/Pipeline.py b/Malt/Pipeline.py index 662cdc3e..fcc9c767 100644 --- a/Malt/Pipeline.py +++ b/Malt/Pipeline.py @@ -1,6 +1,5 @@ -import os +import math, os, ctypes from os import path -import ctypes from Malt.Utils import LOG @@ -305,7 +304,10 @@ def build_scene_batches(self, objects): if i == batch_length or instances_count == max_instances: local_models = ((ctypes.c_float * 16) * instances_count).from_address(ctypes.addressof(models)) - local_ids = (ctypes.c_uint * instances_count).from_address(ctypes.addressof(ids)) + # IDs are stored as uvec4, so we make sure the buffer count is a multiple of 4, + # since some drivers will only bind a full uvec4 (see issue #319) + id_buffer_count = math.ceil(instances_count/4)*4 + local_ids = (ctypes.c_uint * id_buffer_count).from_address(ctypes.addressof(ids)) models_UBO = UBO() ids_UBO = UBO() diff --git a/Malt/PipelineGraph.py b/Malt/PipelineGraph.py index 1f1f7acf..6213fef0 100644 --- a/Malt/PipelineGraph.py +++ b/Malt/PipelineGraph.py @@ -71,12 +71,8 @@ def get_serializable_copy(self): class GLSLGraphIO(PipelineGraphIO): - COMMON_INPUT_TYPES = ['sampler2D', 'usampler2D', 'isampler2D'] - COMMON_OUTPUT_TYPES = [ - 'float','vec2','vec3','vec4', - 'uint','uvec2','uvec3','uvec4', - 'int','ivec2','ivec3','ivec4', - ] + COMMON_INPUT_TYPES = ['sampler2D'] + COMMON_OUTPUT_TYPES = ['float','vec2','vec3','vec4'] def __init__(self, name, define = None, io_wrap=None, dynamic_input_types = [], dynamic_output_types = [], default_dynamic_inputs = {}, default_dynamic_outputs = {}, shader_type=None, custom_output_start_index=0): diff --git a/Malt/Pipelines/NPR_Pipeline/NPR_Pipeline.py b/Malt/Pipelines/NPR_Pipeline/NPR_Pipeline.py index e4fff924..d063a4fb 100644 --- a/Malt/Pipelines/NPR_Pipeline/NPR_Pipeline.py +++ b/Malt/Pipelines/NPR_Pipeline/NPR_Pipeline.py @@ -177,7 +177,7 @@ def setup_graphs(self): name='Light', graph_type=GLSLPipelineGraph.INTERNAL_GRAPH, default_global_scope=_LIGHT_SHADER_HEADER, - default_shader_src="void LIGHT_SHADER(LightShaderInput I, inout LightShaderOutput O) { }", + default_shader_src="void LIGHT_SHADER(vec3 relative_coordinates, vec3 uvw, inout vec3 color, inout float attenuation) { }", graph_io=[ GLSLGraphIO( name='LIGHT_SHADER', diff --git a/Malt/Pipelines/NPR_Pipeline/Nodes/Render/SceneLighting.py b/Malt/Pipelines/NPR_Pipeline/Nodes/Render/SceneLighting.py index 58fbc138..0208e716 100644 --- a/Malt/Pipelines/NPR_Pipeline/Nodes/Render/SceneLighting.py +++ b/Malt/Pipelines/NPR_Pipeline/Nodes/Render/SceneLighting.py @@ -66,6 +66,11 @@ def execute(self, parameters): sample_offset = self.pipeline.get_sample(scene.world_parameters['Samples.Width']) opaque_batches, transparent_batches = self.pipeline.get_scene_batches(scene) + inputs['Spot Resolution'] = max(8, inputs['Spot Resolution']) + inputs['Sun Resolution'] = max(8, inputs['Sun Resolution']) + inputs['Point Resolution'] = max(8, inputs['Point Resolution']) + inputs['Sun CSM Count'] = max(1, inputs['Sun CSM Count']) + self.lights_buffer.load(scene, inputs['Spot Resolution'], inputs['Sun Resolution'], diff --git a/Malt/Pipelines/NPR_Pipeline/Shaders/NPR_LightShader.glsl b/Malt/Pipelines/NPR_Pipeline/Shaders/NPR_LightShader.glsl index 0712079e..9d05a4ea 100644 --- a/Malt/Pipelines/NPR_Pipeline/Shaders/NPR_LightShader.glsl +++ b/Malt/Pipelines/NPR_Pipeline/Shaders/NPR_LightShader.glsl @@ -4,19 +4,6 @@ uniform int LIGHT_INDEX; -struct LightShaderInput -{ - Light L; - LitSurface LS; - vec3 light_space_position; - vec3 light_uv; -}; - -struct LightShaderOutput -{ - vec3 color; -}; - #ifdef VERTEX_SHADER void main() { @@ -30,7 +17,7 @@ uniform sampler2D IN_DEPTH; layout (location = 0) out vec3 RESULT; -void LIGHT_SHADER(LightShaderInput I, inout LightShaderOutput O); +void LIGHT_SHADER(vec3 relative_coordinates, vec3 uvw, inout vec3 color, inout float attenuation); void main() { @@ -43,39 +30,50 @@ void main() Light L = LIGHTS.lights[LIGHT_INDEX]; LitSurface LS = lit_surface(POSITION, vec3(0), L, false); - vec3 light_space; - vec3 light_uv; + vec3 light_space = vec3(0); + vec3 uvw = vec3(0); if(L.type == LIGHT_SPOT) { - light_space = project_point(LIGHTS.spot_matrices[L.type_index], POSITION); - light_uv = light_space * 0.5 + 0.5; + light_space = project_point(LIGHTS.spot_matrices[L.type_index], POSITION); + uvw.xy = light_space.xy * 0.5 + 0.5; } if(L.type == LIGHT_SUN) { - mat4 matrix = LIGHTS.sun_matrices[L.type_index*LIGHTS.cascades_count]; - matrix[3] = vec4(L.position, 1); - light_space = project_point(matrix, POSITION); - light_uv = light_space; + vec3 z = L.direction; + vec3 c = vec3(0,0,1); + if(abs(dot(z, c)) < 1.0) + { + vec3 x = normalize(cross(c, z)); + vec3 y = normalize(cross(x, z)); + mat3 rotation = mat3(x,y,z); + mat4 m = mat4_translation(L.position) * mat4(rotation); + m = inverse(m); + + light_space = transform_point(m, POSITION); + } + else + { + light_space = POSITION; + light_space -= L.position; + } + + uvw.xy = light_space.xy; } if(L.type == LIGHT_POINT) { - light_space = POSITION - L.position; - light_uv = normalize(POSITION - L.position); + light_space = POSITION - L.position; + light_space /= L.radius; + + uvw = normalize(light_space); } - LightShaderInput I; - I.L = L; - I.LS = LS; - I.light_space_position = light_space; - I.light_uv = light_uv; - - LightShaderOutput O; - O.color = LS.light_color; + vec3 color = L.color; + float attenuation = LS.P; - LIGHT_SHADER(I,O); + LIGHT_SHADER(light_space, uvw, color, attenuation); - RESULT = O.color; + RESULT = color * attenuation; } #endif //PIXEL_SHADER diff --git a/Malt/Shaders/Node Utils/vec2.glsl b/Malt/Shaders/Node Utils/vec2.glsl index 5568efd0..dc8f05df 100644 --- a/Malt/Shaders/Node Utils/vec2.glsl +++ b/Malt/Shaders/Node Utils/vec2.glsl @@ -25,6 +25,8 @@ vec2 vec2_max(vec2 a, vec2 b){ return max(a,b); } vec2 vec2_mix(vec2 a, vec2 b, vec2 factor){ return mix(a,b,factor); } vec2 vec2_mix_float(vec2 a, vec2 b, float factor){ return mix(a,b,factor); } +vec2 vec2_normalize(vec2 v){ return normalize(v); } + float vec2_length(vec2 v){ return length(v); } float vec2_distance(vec2 a, vec2 b){ return distance(a,b); } float vec2_dot_product(vec2 a, vec2 b){ return dot(a,b); } diff --git a/Malt/Shaders/Node Utils/vec3.glsl b/Malt/Shaders/Node Utils/vec3.glsl index f29069c0..4ddd4b6c 100644 --- a/Malt/Shaders/Node Utils/vec3.glsl +++ b/Malt/Shaders/Node Utils/vec3.glsl @@ -44,6 +44,9 @@ vec3 vec3_mix(vec3 a, vec3 b, vec3 factor){ return mix(a,b,factor); } /*META @a: subtype=Vector; @b: subtype=Vector;*/ vec3 vec3_mix_float(vec3 a, vec3 b, float factor){ return mix(a,b,factor); } +/*META @v: subtype=Vector;*/ +vec3 vec3_normalize(vec3 v){ return normalize(v); } + /*META @v: subtype=Vector;*/ float vec3_length(vec3 v){ return length(v); } /*META @a: subtype=Vector; @b: subtype=Vector;*/ diff --git a/Malt/Shaders/Node Utils/vec4.glsl b/Malt/Shaders/Node Utils/vec4.glsl index 2c08ec0c..ba64c341 100644 --- a/Malt/Shaders/Node Utils/vec4.glsl +++ b/Malt/Shaders/Node Utils/vec4.glsl @@ -44,6 +44,9 @@ vec4 vec4_mix(vec4 a, vec4 b, vec4 factor){ return mix(a,b,factor); } /*META @a: subtype=Vector; @b: subtype=Vector;*/ vec4 vec4_mix_float(vec4 a, vec4 b, float factor){ return mix(a,b,factor); } +/*META @v: subtype=Vector;*/ +vec4 vec4_normalize(vec4 v){ return normalize(v); } + /*META @v: subtype=Vector;*/ float vec4_length(vec4 v){ return length(v); } /*META @a: subtype=Vector; @b: subtype=Vector;*/ diff --git a/Malt/Shaders/Procedural/Fractal_Noise.glsl b/Malt/Shaders/Procedural/Fractal_Noise.glsl index f3d19ea4..f9796664 100644 --- a/Malt/Shaders/Procedural/Fractal_Noise.glsl +++ b/Malt/Shaders/Procedural/Fractal_Noise.glsl @@ -11,16 +11,19 @@ vec4 fractal_noise_ex(vec4 coord, int octaves, bool tile, vec4 tile_size) { vec4 result = vec4(0); - float strength = 0.5; + float strength = 1.0; + float total_strength = 0.0; for (int i = 0; i < octaves; i++) - { - result += strength * noise_ex(coord, tile, tile_size); + { + vec4 noise = noise_ex(coord, tile, tile_size); + result += strength * noise; + total_strength += strength; + strength *= 0.5; coord *= 2.0; tile_size *= 2.0; - strength *= 0.5; } - return result; + return result / total_strength; } diff --git a/README.md b/README.md index e085418f..07bd8478 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Malt is a fully customizable real-time rendering framework for animation and illustration. It's aimed at advanced users and technical artists who want more control over their workflow and/or their art style, with special care put into the needs of stylized non photorealistic rendering. -[Docs](https://malt3d.com) | [Forums & Support](https://github.com/bnpr/Malt/discussions) | [Bug Reports](https://github.com/bnpr/Malt/issues) | [Twitter](https://twitter.com/pragma37) | [Patreon](https://patreon.com/pragma37) +[Download](#install) | [Docs](https://malt3d.com) | [Forums & Support](https://github.com/bnpr/Malt/discussions) | [Bug Reports](https://github.com/bnpr/Malt/issues) | [Twitter](https://twitter.com/pragma37) | [Patreon](https://patreon.com/pragma37) ## Features @@ -20,7 +20,8 @@ It's aimed at advanced users and technical artists who want more control over th ## Current State -The 1.0 Release is almost ready, take a look at the [1.0 Preview](https://github.com/bnpr/Malt/discussions/231) and leave your feedback. +We've been working on a full redesign of the default nodes before the 1.0 Release. +[Give it try and leave your feedback](https://github.com/bnpr/Malt/discussions/382). Malt is software agnostic, but Blender is the only integration planned right now. @@ -28,6 +29,7 @@ Malt is software agnostic, but Blender is the only integration planned right now - OpenGL 4.1+ - Blender 3.2 +- Windows or Linux > A dedicated Nvidia or AMD graphics card is highly recomended. @@ -48,10 +50,8 @@ Malt is software agnostic, but Blender is the only integration planned right now ## First steps -To learn how to use *Malt*, check [this playlist](https://www.youtube.com/playlist?list=PLiN2BGdwwlLqbks8h5MohvH0Xd0Zql_Sg) and the [Sample Files](https://github.com/bnpr/Malt/discussions/94). - -> Malt allows to use different settings for *Viewport Preview*, *Viewport Render* and *F12 Render*. -> By default, the *Viewport Preview* should be faster than the *Viewport Render* mode. +To learn how to use *Malt*, check the [Docs](https://malt3d.com/Documentation/Getting%20Started/), this [playlist](https://www.youtube.com/playlist?list=PLiN2BGdwwlLqbks8h5MohvH0Xd0Zql_Sg) and the [Sample Files](https://github.com/bnpr/Malt/discussions/94). +The [Q&A section](https://github.com/bnpr/Malt/discussions/categories/q-a) is full of info as well. ## Developer Documentation diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..c07b0c6b --- /dev/null +++ b/__init__.py @@ -0,0 +1,20 @@ +# Some people are used to download the zipped repo from Github to install Blender addons. +# This file is just to redirect those users to the right path. It's not distributed in the actual addon. + +bl_info = { + "name": "Oops! You downloaded the wrong BlenderMalt file.", + "description" : "Please, read the install intructions on Github or malt3d.com", + "author" : "Miguel Pozo", + "version": (1,0,0), + "blender" : (3, 0, 0), + "doc_url": "https://malt3d.com/documentation/getting started/#install", + "tracker_url": "https://github.com/bnpr/Malt#install", + "category": "Render" +} + +def register(): + pass + +def unregister(): + pass + diff --git a/docs/Documentation/Plugins.md b/docs/Documentation/Plugins.md index f2d21b11..e6ecddc9 100644 --- a/docs/Documentation/Plugins.md +++ b/docs/Documentation/Plugins.md @@ -2,6 +2,7 @@ Render pipelines can be customized through plugins. Plugins can: + - Add new node libraries to *Pipeline Graphs*. - Add new *Pipeline Parameters*. - Add new *PipelineGraph types*. diff --git a/docs/Documentation/Settings.md b/docs/Documentation/Settings.md index 45b75a0a..62c8a43e 100644 --- a/docs/Documentation/Settings.md +++ b/docs/Documentation/Settings.md @@ -8,12 +8,14 @@ >Opens the current session log in a text editor. - **Global Plugins** >The path to the *plugins* folder. See [Plugins](../Plugins) for more info. +- **Show sockets in Material Panel** +>Show node socket properties in the Material Panel by default. +- **Max Viewport Render Framerate** +>Framerate cap for the viewport. Limiting *Blender* framerate can improve *Malt* performance and animation playback stability. Set it to 0 to disable it. - **Auto setup VSCode** >On file save, setups a VSCode project on your *.blend* file folder. - **RenderDoc Path** >Path to the **renderdoccmd** executable, for [RenderDoc](https://renderdoc.org/) debugging. -- **Max Viewport Render Framerate** ->Framerate cap for the viewport. Limiting *Blender* framerate can improve *Malt* performance and animation playback stability. Set it to 0 to disable it. - **Debug Mode** >Include debug info in the *Session Logs*. Enabling it increases the log sizes and can negatively affect performance, don't enable it unless a developer asks you for it in a bug report. diff --git a/docs/reference/Light-graph.md b/docs/reference/Light-graph.md index f59b3e4a..86ebeb00 100644 --- a/docs/reference/Light-graph.md +++ b/docs/reference/Light-graph.md @@ -1438,6 +1438,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec2 )* --- +### **vec2_normalize** +>vec2 vec2_normalize(vec2 v) + +- **Inputs** + - **v** *: ( vec2 )* +- **Outputs** + - **result** *: ( vec2 )* +--- ### **vec2_length** >float vec2_length(vec2 v) @@ -1679,6 +1687,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec3 )* --- +### **vec3_normalize** +>vec3 vec3_normalize(vec3 v) + +- **Inputs** + - **v** *: ( vec3 | Vector )* +- **Outputs** + - **result** *: ( vec3 )* +--- ### **vec3_length** >float vec3_length(vec3 v) @@ -1931,6 +1947,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec4 )* --- +### **vec4_normalize** +>vec4 vec4_normalize(vec4 v) + +- **Inputs** + - **v** *: ( vec4 | Vector )* +- **Outputs** + - **result** *: ( vec4 )* +--- ### **vec4_length** >float vec4_length(vec4 v) diff --git a/docs/reference/Mesh-graph.md b/docs/reference/Mesh-graph.md index 26eb04b7..4e65db13 100644 --- a/docs/reference/Mesh-graph.md +++ b/docs/reference/Mesh-graph.md @@ -1741,6 +1741,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec2 )* --- +### **vec2_normalize** +>vec2 vec2_normalize(vec2 v) + +- **Inputs** + - **v** *: ( vec2 )* +- **Outputs** + - **result** *: ( vec2 )* +--- ### **vec2_length** >float vec2_length(vec2 v) @@ -1982,6 +1990,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec3 )* --- +### **vec3_normalize** +>vec3 vec3_normalize(vec3 v) + +- **Inputs** + - **v** *: ( vec3 | Vector )* +- **Outputs** + - **result** *: ( vec3 )* +--- ### **vec3_length** >float vec3_length(vec3 v) @@ -2234,6 +2250,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec4 )* --- +### **vec4_normalize** +>vec4 vec4_normalize(vec4 v) + +- **Inputs** + - **v** *: ( vec4 | Vector )* +- **Outputs** + - **result** *: ( vec4 )* +--- ### **vec4_length** >float vec4_length(vec4 v) diff --git a/docs/reference/Render Layer-graph.md b/docs/reference/Render Layer-graph.md index 7893f31a..01646ccc 100644 --- a/docs/reference/Render Layer-graph.md +++ b/docs/reference/Render Layer-graph.md @@ -27,18 +27,6 @@ Performs anti-aliasing by accumulating multiple render samples into a single tex - **Outputs** - **Color** *: ( Texture )* --- -### **Unpack8bitTextures** -Unpacks up to 4 textures packed into a single one using the *pack_8bit* shader function. -*(Useful when a shader needs to output more than 8 textures)* - -- **Inputs** - - **Packed Texture** *: ( usampler2D )* -- **Outputs** - - **A** *: ( Texture )* - - **B** *: ( Texture )* - - **C** *: ( Texture )* - - **D** *: ( Texture )* ---- ### **MainPass** >Graph Type / Pass : *Mesh / MAIN_PASS_PIXEL_SHADER* diff --git a/docs/reference/Render-graph.md b/docs/reference/Render-graph.md index 3f7e30de..453cfa50 100644 --- a/docs/reference/Render-graph.md +++ b/docs/reference/Render-graph.md @@ -27,18 +27,6 @@ Performs anti-aliasing by accumulating multiple render samples into a single tex - **Outputs** - **Color** *: ( Texture )* --- -### **Unpack8bitTextures** -Unpacks up to 4 textures packed into a single one using the *pack_8bit* shader function. -*(Useful when a shader needs to output more than 8 textures)* - -- **Inputs** - - **Packed Texture** *: ( usampler2D )* -- **Outputs** - - **A** *: ( Texture )* - - **B** *: ( Texture )* - - **C** *: ( Texture )* - - **D** *: ( Texture )* ---- ### **RenderLayers** >Graph Type / Pass : *Render Layer / Render Layer* diff --git a/docs/reference/Screen-graph.md b/docs/reference/Screen-graph.md index 996ca7f5..e21d247e 100644 --- a/docs/reference/Screen-graph.md +++ b/docs/reference/Screen-graph.md @@ -1652,6 +1652,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec2 )* --- +### **vec2_normalize** +>vec2 vec2_normalize(vec2 v) + +- **Inputs** + - **v** *: ( vec2 )* +- **Outputs** + - **result** *: ( vec2 )* +--- ### **vec2_length** >float vec2_length(vec2 v) @@ -1893,6 +1901,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec3 )* --- +### **vec3_normalize** +>vec3 vec3_normalize(vec3 v) + +- **Inputs** + - **v** *: ( vec3 | Vector )* +- **Outputs** + - **result** *: ( vec3 )* +--- ### **vec3_length** >float vec3_length(vec3 v) @@ -2145,6 +2161,14 @@ Blends the blend color as a layer over the base color. - **Outputs** - **result** *: ( vec4 )* --- +### **vec4_normalize** +>vec4 vec4_normalize(vec4 v) + +- **Inputs** + - **v** *: ( vec4 | Vector )* +- **Outputs** + - **result** *: ( vec4 )* +--- ### **vec4_length** >float vec4_length(vec4 v) diff --git a/scripts/install_dependencies.py b/scripts/install_dependencies.py index 5306296c..de7849c8 100644 --- a/scripts/install_dependencies.py +++ b/scripts/install_dependencies.py @@ -12,10 +12,10 @@ py_version = str(sys.version_info[0])+str(sys.version_info[1]) malt_dependencies_path = os.path.join(malt_folder, '.Dependencies-{}'.format(py_version)) -dependencies = ['glfw', 'PyOpenGL==3.1.5', 'PyOpenGL_accelerate==3.1.5', 'Pyrr', 'psutil'] +dependencies = ['glfw', 'PyOpenGL', 'PyOpenGL_accelerate', 'Pyrr', 'psutil', 'xxhash'] for dependency in dependencies: try: - subprocess.check_call([sys.executable, '-m', 'pip', 'install', dependency, '--target', malt_dependencies_path]) + subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--upgrade', dependency, '--target', malt_dependencies_path]) except: print('ERROR: pip install {} failed.'.format(dependency)) import traceback