├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml └── workflows │ └── BlenderMalt.yml ├── .gitignore ├── BlenderMalt ├── CBlenderMalt │ ├── CBlenderMalt.cpp │ ├── CMakeLists.txt │ ├── __init__.py │ ├── build.py │ ├── mikktspace.c │ └── mikktspace.h ├── MaltLights.py ├── MaltMaterial.py ├── MaltMeshes.py ├── MaltNodes │ ├── MaltCustomPasses.py │ ├── MaltNode.py │ ├── MaltNodeTree.py │ ├── MaltNodeUITools.py │ ├── MaltSocket.py │ ├── Nodes │ │ ├── MaltArrayIndexNode.py │ │ ├── MaltFunctionNode.py │ │ ├── MaltFunctionSubCategory.py │ │ ├── MaltIONode.py │ │ ├── MaltInlineNode.py │ │ └── MaltStructNode.py │ └── _init_.py ├── MaltPipeline.py ├── MaltProperties.py ├── MaltRenderEngine.py ├── MaltTextures.py ├── MaltUtils.py ├── __init__.py └── readme.md ├── Bridge ├── Client_API.py ├── Docs.py ├── Material.py ├── Mesh.py ├── Proxys.py ├── Server.py ├── Texture.py ├── __init__.py ├── ipc │ ├── CMakeLists.txt │ ├── __init__.py │ ├── build.py │ ├── ipc.c │ └── ipc.h └── renderdoc │ ├── CMakeLists.txt │ ├── __init__.py │ ├── build.py │ ├── renderdoc_app.h │ └── renderdoc_wrapper.c ├── LICENSE ├── LICENSE - DEPENDENCIES ├── LICENSE - DEPENDENCIES (FULL TEXT) ├── Malt ├── GL │ ├── GL.py │ ├── GLSLEval.py │ ├── GLSLParser │ │ ├── CMakeLists.txt │ │ ├── build.py │ │ ├── external │ │ │ ├── CMakeLists.txt │ │ │ ├── PEGTL-LICENSE │ │ │ └── rapidjson-LICENSE │ │ └── src │ │ │ └── main.cpp │ ├── Mesh.py │ ├── RenderTarget.py │ ├── Shader.py │ ├── Texture.py │ └── readme.md ├── Nodes │ ├── LineRender.py │ ├── SceneFilter.py │ └── SuperSamplingAA.py ├── Pipeline.py ├── PipelineGraph.py ├── PipelineNode.py ├── PipelineParameters.py ├── PipelinePlugin.py ├── Pipelines │ ├── MiniPipeline │ │ └── MiniPipeline.py │ └── NPR_Pipeline │ │ ├── Defaults │ │ └── defaults.blend │ │ ├── NPR_LightShaders.py │ │ ├── NPR_Lighting.py │ │ ├── NPR_Pipeline.py │ │ ├── Nodes │ │ ├── Render │ │ │ ├── RenderLayers.py │ │ │ ├── SceneLighting.py │ │ │ └── ScreenPass.py │ │ └── RenderLayer │ │ │ ├── MainPass.py │ │ │ ├── PrePass.py │ │ │ └── ScreenPass.py │ │ └── Shaders │ │ ├── NPR_Intellisense.glsl │ │ ├── NPR_LightShader.glsl │ │ ├── NPR_MeshShader.glsl │ │ ├── NPR_Pipeline │ │ ├── NPR_Filters.glsl │ │ ├── NPR_Lighting.glsl │ │ ├── NPR_Mesh.glsl │ │ ├── NPR_Shading.glsl │ │ └── NPR_Shading2.glsl │ │ └── NPR_ScreenShader.glsl ├── Render │ ├── Common.py │ ├── DepthToCompositeDepth.py │ ├── Lighting.py │ ├── Sampling.py │ └── readme.md ├── Scene.py ├── Shaders │ ├── Common.glsl │ ├── Common │ │ ├── Color.glsl │ │ ├── Hash.glsl │ │ ├── Mapping.glsl │ │ ├── Math.glsl │ │ ├── Matrix.glsl │ │ ├── Normal.glsl │ │ ├── Quaternion.glsl │ │ └── Transform.glsl │ ├── Filters │ │ ├── AO.glsl │ │ ├── Bevel.glsl │ │ ├── Blur.glsl │ │ ├── Curvature.glsl │ │ ├── JumpFlood.glsl │ │ ├── Kuwahara.glsl │ │ ├── Line.glsl │ │ ├── Sharpen.glsl │ │ └── StructureTensor.glsl │ ├── Intellisense │ │ └── intellisense.glsl │ ├── Lighting │ │ └── Lighting.glsl │ ├── Node Utils 2 │ │ ├── Bool.glsl │ │ ├── Color.glsl │ │ ├── ColorBlend.glsl │ │ ├── Filter.glsl │ │ ├── Float.glsl │ │ ├── Input.glsl │ │ ├── Int.glsl │ │ ├── Mat4.glsl │ │ ├── Parameters.glsl │ │ ├── Texturing.glsl │ │ ├── Vec2.glsl │ │ ├── Vec3.glsl │ │ ├── Vec4.glsl │ │ ├── Vector.glsl │ │ ├── conversion.glsl │ │ └── node_utils_2.glsl │ ├── Node Utils │ │ ├── bool.glsl │ │ ├── common.glsl │ │ ├── float.glsl │ │ ├── int.glsl │ │ ├── node_utils.glsl │ │ ├── packing.glsl │ │ ├── properties.glsl │ │ ├── sampler.glsl │ │ ├── vec2.glsl │ │ ├── vec3.glsl │ │ └── vec4.glsl │ ├── Passes │ │ ├── BlendTexture.glsl │ │ ├── BlendTransparency.glsl │ │ ├── CopyTextures.glsl │ │ ├── DepthToBlenderDepth.glsl │ │ ├── JumpFlood.glsl │ │ ├── LineComposite.glsl │ │ ├── Unpack8bitTextures.glsl │ │ └── sRGBConversion.glsl │ ├── Procedural │ │ ├── Bayer.glsl │ │ ├── Cell_Noise.glsl │ │ ├── Cell_Noise.inl │ │ ├── Fractal_Noise.glsl │ │ ├── Fractal_Noise.inl │ │ ├── Noise.glsl │ │ └── Noise.inl │ ├── SDF │ │ └── SDF.glsl │ ├── Shading │ │ ├── BRDF.glsl │ │ └── ShadingModels.glsl │ └── readme.md ├── SourceTranspiler.py ├── Utils.py ├── __init__.py └── readme.md ├── README.md ├── __init__.py ├── docs ├── Documentation │ ├── 2022-02-16-16-45-47.png │ ├── 2022-02-16-17-05-31.png │ ├── 2022-02-16-17-08-23.png │ ├── 2022-02-16-17-20-18.png │ ├── 2022-02-23-16-58-56.png │ ├── 2022-02-23-17-03-18.png │ ├── 2022-02-23-17-11-03.png │ ├── 2022-02-25-19-56-34.png │ ├── 2022-02-25-20-18-44.png │ ├── 2022-03-21-15-36-42.png │ ├── 2022-03-21-15-58-04.png │ ├── 2022-03-21-18-05-53.png │ ├── 2022-03-21-18-11-27.png │ ├── 2022-03-21-18-12-34.png │ ├── 2022-03-21-18-17-30.png │ ├── 2022-03-21-19-27-33.png │ ├── 2022-03-21-19-36-40.png │ ├── 2022-04-02-17-34-35.png │ ├── 2022-04-02-17-35-20.png │ ├── 2022-04-02-17-36-17.png │ ├── 2022-04-02-18-02-24.png │ ├── Getting Started.md │ ├── Graphs.md │ ├── Plugins.md │ ├── Settings.md │ ├── Tooling.md │ ├── mesh-passes.svg │ └── parameter-override.gif ├── FAQ.md ├── From-Nodes-To-Code │ ├── From-Nodes-To-Code.md │ ├── all-together.png │ ├── flat-color.png │ ├── lighting.png │ ├── starting-point.png │ └── textures.png ├── Setup-BlenderMalt-for-Development.md ├── extra │ ├── extra.css │ └── extra.js ├── images │ ├── become_a_patron.png │ ├── creativeshrimp_logo.png │ └── cube-solid.svg ├── index.md ├── overrides │ └── partials │ │ └── _footer.html └── reference │ ├── Light-graph.md │ ├── Mesh-graph.md │ ├── Render Layer-graph.md │ ├── Render-graph.md │ ├── Screen-graph.md │ └── settings.md ├── mkdocs ├── mkdocs.yml ├── netlify.toml └── requirements.txt ├── plugins ├── Experimental │ ├── RenderLayer │ │ └── OpaqueLayer.py │ └── __init__.py └── PluginExample │ ├── Shaders │ └── PluginExample.glsl │ └── __init__.py └── scripts ├── PatchDependencies ├── mcpp-Darwin ├── mcpp-Linux ├── mcpp-Windows.exe ├── python-gpu-310.exe ├── python-gpu-311.exe ├── python-gpu-37.exe ├── python-gpu-39.exe └── python-gpu.c ├── build_intellisense_glsl.py ├── format.py ├── generate_conversion_nodes.py ├── generate_license_dependencies_full.py ├── get_glslang.py ├── install_dependencies.py ├── print_pixel_formats.py ├── settings.json └── setup_blender_addon.py /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Reports 2 | description: Open a new issue 3 | body: 4 | 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before opening a new issue: 9 | - Read the [readme](https://github.com/bnpr/Malt#malt), check that you meet the minimum requirements and have installed Malt properly. 10 | - Make sure you are running the latest Malt version. 11 | - Search [existing issues](https://github.com/bnpr/Malt/issues) to ensure it has not already been reported. 12 | 13 | - type: input 14 | attributes: 15 | label: Malt version 16 | placeholder: Release | Development 17 | validations: 18 | required: true 19 | 20 | - type: input 21 | attributes: 22 | label: Blender version 23 | placeholder: Blender 3.0.0 24 | validations: 25 | required: true 26 | 27 | - type: input 28 | attributes: 29 | label: OS 30 | placeholder: Windows 10 64 bits 31 | validations: 32 | required: true 33 | 34 | - type: input 35 | attributes: 36 | label: Hardware info 37 | placeholder: Intel i7-4790K | 16GB RAM | Nvidia GTX 1060 6GB 38 | validations: 39 | required: true 40 | 41 | - type: textarea 42 | attributes: 43 | label: Issue description and reproduction steps 44 | validations: 45 | required: true 46 | 47 | - type: textarea 48 | attributes: 49 | label: Attachments 50 | description: | 51 | - The **Log file** from the Blender session where the bug happened. 52 | You can find it in *Preferences > Addons > BlenderMalt > Open Session Log* or search for it in your system temporary folder. 53 | - A zipped **.blend file** with a minimal example to reproduce the error. *(Unless the error is not file dependent)* 54 | > Don't copy/paste the *Log* text. Drag and drop the files to upload them. 55 | validations: 56 | required: true 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Q&A 5 | url: https://github.com/bnpr/Malt/discussions/categories/q-a 6 | about: Ask your *How to* questions. 7 | 8 | - name: Feedback & Feature Requests 9 | url: https://github.com/bnpr/Malt/discussions/categories/feedback-feature-requests 10 | about: Post your feedback and feature requests. 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | !.gitattributes 4 | !.gitmodules 5 | 6 | !.github 7 | 8 | __pycache__ 9 | 10 | *.lib 11 | *.dll 12 | *.so 13 | 14 | *.log 15 | 16 | -------------------------------------------------------------------------------- /BlenderMalt/CBlenderMalt/CBlenderMalt.cpp: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "mikktspace.h" 3 | 4 | #ifdef _WIN32 5 | #define EXPORT extern "C" __declspec( dllexport ) 6 | #else 7 | #define EXPORT extern "C" __attribute__ ((visibility ("default"))) 8 | #endif 9 | 10 | EXPORT void retrieve_mesh_data( 11 | float* in_positions, 12 | int* in_loop_verts, int loop_count, 13 | int* in_loop_tris, 14 | int* in_loop_tri_polys, int loop_tri_count, 15 | int* in_mat_indices, 16 | float* out_positions, unsigned int** out_indices, unsigned int* out_index_lengths) 17 | { 18 | for(int i = 0; i < loop_count; i++) 19 | { 20 | out_positions[i*3+0] = in_positions[in_loop_verts[i]*3+0]; 21 | out_positions[i*3+1] = in_positions[in_loop_verts[i]*3+1]; 22 | out_positions[i*3+2] = in_positions[in_loop_verts[i]*3+2]; 23 | } 24 | 25 | unsigned int* mat_i = out_index_lengths; 26 | 27 | for(int i = 0; i < loop_tri_count; i++) 28 | { 29 | int mat = in_mat_indices ? in_mat_indices[in_loop_tri_polys[i]] : 0; 30 | out_indices[mat][mat_i[mat]++] = in_loop_tris[i*3+0]; 31 | out_indices[mat][mat_i[mat]++] = in_loop_tris[i*3+1]; 32 | out_indices[mat][mat_i[mat]++] = in_loop_tris[i*3+2]; 33 | } 34 | } 35 | 36 | EXPORT bool mesh_tangents( 37 | int* in_indices, int index_len, 38 | float* in_positions, float* in_normals, float* in_uvs, 39 | float* out_tangents) 40 | { 41 | struct MData 42 | { 43 | int* indices; 44 | int index_len; 45 | float* positions; 46 | float* normals; 47 | float* uvs; 48 | float* tangents; 49 | }; 50 | 51 | MData data = { 52 | in_indices, 53 | index_len, 54 | in_positions, 55 | in_normals, 56 | in_uvs, 57 | out_tangents, 58 | }; 59 | 60 | SMikkTSpaceInterface mti = {0}; 61 | mti.m_getNumFaces = [](const SMikkTSpaceContext * pContext){ 62 | return ((MData*)pContext->m_pUserData)->index_len / 3; 63 | }; 64 | 65 | mti.m_getNumVerticesOfFace = [](const SMikkTSpaceContext * pContext, const int iFace){ 66 | return 3; 67 | }; 68 | 69 | mti.m_getPosition = [](const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert){ 70 | MData* m = (MData*)pContext->m_pUserData; 71 | int i = iFace * 3 + iVert; 72 | fvPosOut[0] = m->positions[m->indices[i] * 3 + 0]; 73 | fvPosOut[1] = m->positions[m->indices[i] * 3 + 1]; 74 | fvPosOut[2] = m->positions[m->indices[i] * 3 + 2]; 75 | }; 76 | 77 | mti.m_getNormal = [](const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert){ 78 | MData* m = (MData*)pContext->m_pUserData; 79 | int i = iFace * 3 + iVert; 80 | fvNormOut[0] = m->normals[m->indices[i] * 3 + 0]; 81 | fvNormOut[1] = m->normals[m->indices[i] * 3 + 1]; 82 | fvNormOut[2] = m->normals[m->indices[i] * 3 + 2]; 83 | }; 84 | 85 | mti.m_getTexCoord = [](const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert){ 86 | MData* m = (MData*)pContext->m_pUserData; 87 | int i = iFace * 3 + iVert; 88 | fvTexcOut[0] = m->uvs[m->indices[i] * 2 + 0]; 89 | fvTexcOut[1] = m->uvs[m->indices[i] * 2 + 1]; 90 | }; 91 | 92 | mti.m_setTSpaceBasic = [](const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert){ 93 | MData* m = (MData*)pContext->m_pUserData; 94 | int i = iFace * 3 + iVert; 95 | m->tangents[m->indices[i] * 4 + 0] = fvTangent[0]; 96 | m->tangents[m->indices[i] * 4 + 1] = fvTangent[1]; 97 | m->tangents[m->indices[i] * 4 + 2] = fvTangent[2]; 98 | m->tangents[m->indices[i] * 4 + 3] = fSign; 99 | }; 100 | 101 | SMikkTSpaceContext mtc; 102 | mtc.m_pInterface = &mti; 103 | mtc.m_pUserData = &data; 104 | 105 | return genTangSpaceDefault(&mtc); 106 | } 107 | -------------------------------------------------------------------------------- /BlenderMalt/CBlenderMalt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | SET(CMAKE_CXX_STANDARD 17) 4 | # set(CMAKE_GENERATOR_PLATFORM x64) 5 | 6 | project(CBlenderMalt) 7 | 8 | SET(CMAKE_BUILD_TYPE Release) 9 | SET(BUILD_SHARED_LIBS ON) 10 | 11 | add_library(CBlenderMalt CBlenderMalt.cpp mikktspace.c) 12 | target_include_directories(CBlenderMalt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/blender_dna) 13 | 14 | install(TARGETS CBlenderMalt CONFIGURATIONS Release DESTINATION ${PROJECT_SOURCE_DIR}) 15 | -------------------------------------------------------------------------------- /BlenderMalt/CBlenderMalt/__init__.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import ctypes 4 | 5 | import platform 6 | 7 | src_dir = os.path.abspath(os.path.dirname(__file__)) 8 | 9 | library = 'libCBlenderMalt.so' 10 | if platform.system() == 'Windows': library = 'CBlenderMalt.dll' 11 | if platform.system() == 'Darwin': library = 'libCBlenderMalt.dylib' 12 | 13 | CBlenderMalt = ctypes.CDLL(os.path.join(src_dir, library)) 14 | 15 | retrieve_mesh_data = CBlenderMalt['retrieve_mesh_data'] 16 | retrieve_mesh_data.argtypes = [ 17 | ctypes.POINTER(ctypes.c_float), 18 | ctypes.POINTER(ctypes.c_int), ctypes.c_int, 19 | ctypes.POINTER(ctypes.c_int), 20 | ctypes.POINTER(ctypes.c_int), ctypes.c_int, 21 | ctypes.POINTER(ctypes.c_int), 22 | ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint32) 23 | ] 24 | retrieve_mesh_data.restype = None 25 | 26 | mesh_tangents = CBlenderMalt['mesh_tangents'] 27 | mesh_tangents.argtypes = [ 28 | ctypes.POINTER(ctypes.c_int), ctypes.c_int, 29 | ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float), 30 | ctypes.POINTER(ctypes.c_float) 31 | ] 32 | mesh_tangents.restype = ctypes.c_bool 33 | -------------------------------------------------------------------------------- /BlenderMalt/CBlenderMalt/build.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import platform 4 | 5 | src_dir = os.path.abspath(os.path.dirname(__file__)) 6 | build_dir = os.path.join(src_dir, '.build') 7 | 8 | try: os.mkdir(build_dir) 9 | except: pass 10 | 11 | if platform.system() == 'Windows': #Multi-config generators, like Visual Studio 12 | subprocess.check_call(['cmake', '-A', 'x64', '..'], cwd=build_dir) 13 | subprocess.check_call(['cmake', '--build', '.', '--config', 'Release'], cwd=build_dir) 14 | else: #Single-config generators 15 | subprocess.check_call(['cmake', '..'], cwd=build_dir) 16 | subprocess.check_call(['cmake', '--build', '.'], cwd=build_dir) 17 | 18 | subprocess.check_call(['cmake', '--install', '.'], cwd=build_dir) 19 | -------------------------------------------------------------------------------- /BlenderMalt/MaltLights.py: -------------------------------------------------------------------------------- 1 | import math 2 | import bpy 3 | 4 | class MaltLight(bpy.types.PropertyGroup): 5 | 6 | def sync_data(self, context): 7 | light = self.id_data 8 | #light.shadow_soft_size = self.radius 9 | if light.type == 'SPOT': 10 | light.cutoff_distance = self.radius 11 | light.spot_size = self.spot_angle 12 | light.spot_blend = self.spot_blend_angle / self.spot_angle 13 | 14 | strength : bpy.props.FloatProperty(name='Strength', default=1, 15 | options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, override={'LIBRARY_OVERRIDABLE'}) 16 | 17 | override_global_settings : bpy.props.BoolProperty(name='Override Global Settings', default=False, 18 | options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) 19 | max_distance : bpy.props.FloatProperty(name='Max Distance', default=100, 20 | options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, override={'LIBRARY_OVERRIDABLE'}) 21 | 22 | radius : bpy.props.FloatProperty( 23 | name='Radius', 24 | default=5, 25 | update=sync_data, 26 | options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, 27 | override={'LIBRARY_OVERRIDABLE'} 28 | ) 29 | spot_angle : bpy.props.FloatProperty( 30 | name='Angle', 31 | default=1, 32 | subtype='ANGLE', 33 | min=0, 34 | max=math.pi, 35 | update=sync_data, 36 | options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, 37 | override={'LIBRARY_OVERRIDABLE'} 38 | ) 39 | spot_blend_angle : bpy.props.FloatProperty( 40 | name='Blend', 41 | default=0.1, 42 | subtype='ANGLE', 43 | min=0, 44 | max=math.pi, 45 | update=sync_data, 46 | options={'LIBRARY_EDITABLE', 'ANIMATABLE'}, 47 | override={'LIBRARY_OVERRIDABLE'} 48 | ) 49 | 50 | def draw_ui(self, layout): 51 | layout.prop(self.id_data, 'color') 52 | layout.prop(self, 'strength') 53 | if self.id_data.type == 'SUN': 54 | layout.prop(self, 'override_global_settings') 55 | if self.override_global_settings: 56 | layout.prop(self, 'max_distance') 57 | if self.id_data.type != 'SUN': 58 | layout.prop(self, 'radius') 59 | if self.id_data.type == 'SPOT': 60 | layout.prop(self, 'spot_angle') 61 | layout.prop(self, 'spot_blend_angle') 62 | 63 | classes = [ 64 | MaltLight 65 | ] 66 | 67 | def register(): 68 | for _class in classes: bpy.utils.register_class(_class) 69 | bpy.types.Light.malt = bpy.props.PointerProperty(type=MaltLight, 70 | options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) 71 | 72 | def unregister(): 73 | for _class in reversed(classes): bpy.utils.unregister_class(_class) 74 | del bpy.types.Light.malt 75 | -------------------------------------------------------------------------------- /BlenderMalt/MaltNodes/Nodes/MaltArrayIndexNode.py: -------------------------------------------------------------------------------- 1 | from Malt.PipelineParameters import Parameter, Type 2 | import bpy 3 | from BlenderMalt.MaltNodes.MaltNode import MaltNode 4 | 5 | 6 | class MaltArrayIndexNode(bpy.types.Node, MaltNode): 7 | 8 | bl_label = "Array Index Node" 9 | 10 | def malt_init(self): 11 | self.setup() 12 | 13 | def malt_setup(self, copy=None): 14 | self.setup_sockets({ 'array' : {'type': '', 'size': 1}, 'index' : {'type': Parameter(0, Type.INT) }}, 15 | {'element' : {'type': ''} }, copy=copy) 16 | 17 | def malt_update(self): 18 | inputs = { 19 | 'array' : {'type': '', 'size': 1}, 20 | 'index' : {'type': 'int', 'meta':{'value':'0'} } 21 | } 22 | outputs = { 'element' : {'type': ''} } 23 | 24 | linked = self.inputs['array'].get_linked() 25 | if linked and linked.array_size > 0: 26 | inputs['array']['type'] = linked.data_type 27 | inputs['array']['size'] = linked.array_size 28 | outputs['element']['type'] = linked.data_type 29 | 30 | self.setup_sockets(inputs, outputs) 31 | 32 | def get_source_socket_reference(self, socket): 33 | return '{}_0_{}'.format(self.get_source_name(), socket.name) 34 | 35 | def get_source_code(self, transpiler): 36 | array = self.inputs['array'] 37 | index = self.inputs['index'] 38 | element = self.outputs['element'] 39 | element_reference = index.get_source_global_reference() 40 | if index.get_linked(): 41 | element_reference = index.get_linked().get_source_reference() 42 | initialization = '{}[{}]'.format(array.get_linked().get_source_reference(), element_reference) 43 | return transpiler.declaration(element.data_type, element.array_size, element.get_source_reference(), initialization) 44 | 45 | 46 | classes = [ 47 | MaltArrayIndexNode, 48 | ] 49 | 50 | def register(): 51 | for _class in classes: bpy.utils.register_class(_class) 52 | 53 | def unregister(): 54 | for _class in reversed(classes): bpy.utils.unregister_class(_class) 55 | -------------------------------------------------------------------------------- /BlenderMalt/MaltNodes/Nodes/MaltFunctionSubCategory.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from BlenderMalt.MaltNodes.Nodes.MaltFunctionNode import MaltFunctionNodeBase 3 | 4 | class MaltFunctionSubCategoryNode(bpy.types.Node, MaltFunctionNodeBase): 5 | 6 | bl_label = "Function SubCategory Node" 7 | 8 | subcategory : bpy.props.StringProperty(options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) 9 | 10 | def get_function_enums(self, context=None): 11 | library = self.id_data.get_full_library() 12 | items = [] 13 | for key in library['subcategories'][self.subcategory]: 14 | function = library['functions'].get(key) 15 | if function is None or function['meta'].get('internal'): 16 | continue 17 | label = function['meta'].get('label') 18 | if label is None: 19 | label = function['name'].replace('_',' ').title() 20 | items.append((key, label, label)) 21 | return items 22 | 23 | def update_function_enum(self, context=None): 24 | self.function_type = self.function_enum 25 | if self.disable_updates == False: 26 | self.id_data.update_ext(force_update=True) 27 | 28 | function_enum : bpy.props.EnumProperty(name='', items=get_function_enums, update=update_function_enum, 29 | options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) 30 | 31 | def malt_setup(self, copy=None): 32 | #Keep the correct function when the subcategory list changes 33 | if self.function_type != '' and self.function_type != self.function_enum: 34 | try: self.function_enum = self.function_type 35 | except: 36 | try: 37 | self.function_type = self.function_enum 38 | except: 39 | self.function_type = self.get_function_enums()[0][0] 40 | return super().malt_setup(copy=copy) 41 | 42 | def should_delete_outdated_links(self): 43 | return True 44 | 45 | def draw_buttons(self, context, layout): 46 | r = layout.row(align=True) 47 | r.context_pointer_set('active_node', self) 48 | r.prop(self, 'function_enum') 49 | if context.preferences.addons['BlenderMalt'].preferences.use_subfunction_cycling: 50 | r.operator('wm.malt_cycle_sub_categories', text='', icon='COLLAPSEMENU') 51 | return super().draw_buttons(context, layout) 52 | 53 | def calc_node_width(self, point_size) -> float: 54 | import blf 55 | blf.size(0, point_size) 56 | max_width = super().calc_node_width(point_size) 57 | layout_padding = 70 # account for the spaces on both sides of the enum dropdown 58 | label = next(enum[1] for enum in self.get_function_enums() if enum[0]==self.function_enum) 59 | return max(max_width, blf.dimensions(0, label)[0] + layout_padding) 60 | 61 | def draw_label(self): 62 | label = super().draw_label() 63 | if self.hide: 64 | label += ' - ' + self.get_function()['meta'].get('label', None) 65 | return label 66 | 67 | 68 | def register(): 69 | bpy.utils.register_class(MaltFunctionSubCategoryNode) 70 | 71 | def unregister(): 72 | bpy.utils.unregister_class(MaltFunctionSubCategoryNode) 73 | -------------------------------------------------------------------------------- /BlenderMalt/MaltNodes/Nodes/MaltInlineNode.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from BlenderMalt.MaltNodes.MaltNode import MaltNode 3 | 4 | 5 | class MaltInlineNode(bpy.types.Node, MaltNode): 6 | 7 | bl_label = "Inline Code Node" 8 | 9 | def code_update(self, context): 10 | #update the node tree 11 | self.id_data.update_ext(force_update=True) 12 | 13 | code : bpy.props.StringProperty(update=code_update, 14 | options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) 15 | 16 | def on_socket_update(self, socket): 17 | self.update() 18 | self.id_data.update_ext(force_update=True) 19 | 20 | def malt_init(self): 21 | self.setup() 22 | 23 | def malt_update(self): 24 | last = 0 25 | for i, input in enumerate(self.inputs): 26 | if input.data_type != '' or input.get_linked(): 27 | last = i + 1 28 | variables = 'abcdefgh'[:min(last+1,8)] 29 | 30 | inputs = {} 31 | for var in variables: 32 | inputs[var] = {'type': ''} 33 | if var in self.inputs: 34 | input = self.inputs[var] 35 | linked = self.inputs[var].get_linked() 36 | if linked and linked.data_type != '': 37 | inputs[var] = {'type': linked.data_type, 'size': linked.array_size} 38 | else: 39 | inputs[var] = {'type': input.data_type, 'size': input.array_size} 40 | 41 | outputs = { 'result' : {'type': ''} } 42 | if 'result' in self.outputs: 43 | out = self.outputs['result'].get_linked() 44 | if out: 45 | outputs['result'] = {'type': out.data_type, 'size': out.array_size} 46 | 47 | self.setup_sockets(inputs, outputs) 48 | 49 | def draw_buttons(self, context, layout): 50 | layout.prop(self, 'code', text='') 51 | 52 | def draw_socket(self, context, layout, socket, text): 53 | if socket.is_output == False: 54 | MaltNode.draw_socket(self, context, layout, socket, socket.name) 55 | layout.prop(socket, 'data_type', text='') 56 | else: 57 | MaltNode.draw_socket(self, context, layout, socket, socket.name) 58 | 59 | def get_source_socket_reference(self, socket): 60 | return '{}_0_{}'.format(self.get_source_name(), socket.name) 61 | 62 | def get_source_code(self, transpiler): 63 | code = '' 64 | result_socket = self.outputs['result'] 65 | code += transpiler.declaration(result_socket.data_type, result_socket.array_size, result_socket.get_source_reference()) 66 | 67 | scoped_code = '' 68 | for input in self.inputs: 69 | if input.data_type != '': 70 | initialization = input.get_source_initialization() 71 | scoped_code += transpiler.declaration(input.data_type, input.array_size, input.name, initialization) 72 | if self.code != '': 73 | scoped_code += transpiler.asignment(self.outputs['result'].get_source_reference(), self.code) 74 | 75 | return code + transpiler.scoped(scoped_code) 76 | 77 | 78 | classes = [ 79 | MaltInlineNode, 80 | ] 81 | 82 | def register(): 83 | for _class in classes: bpy.utils.register_class(_class) 84 | 85 | def unregister(): 86 | for _class in reversed(classes): bpy.utils.unregister_class(_class) 87 | -------------------------------------------------------------------------------- /BlenderMalt/MaltNodes/Nodes/MaltStructNode.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from BlenderMalt.MaltNodes.MaltNode import MaltNode 3 | 4 | 5 | class MaltStructNode(bpy.types.Node, MaltNode): 6 | 7 | bl_label = "Struct Node" 8 | 9 | def malt_setup(self, copy=None): 10 | inputs = {} 11 | inputs[self.struct_type] = {'type' : self.struct_type} 12 | outputs = {} 13 | outputs[self.struct_type] = {'type' : self.struct_type} 14 | 15 | self.setup_sockets(inputs, outputs, copy=copy) 16 | 17 | struct_type : bpy.props.StringProperty(update=MaltNode.setup, 18 | options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'}) 19 | 20 | def get_struct(self): 21 | graph = self.id_data.get_pipeline_graph() 22 | if self.struct_type in graph.structs: 23 | return graph.structs[self.struct_type] 24 | else: 25 | return self.id_data.get_library()['structs'][self.struct_type] 26 | 27 | def get_source_socket_reference(self, socket): 28 | if socket.name == self.struct_type: 29 | return self.get_source_name() 30 | else: 31 | return socket.name.replace(self.struct_type, self.get_source_name()) 32 | 33 | def struct_input_is_linked(self): 34 | return self.inputs[self.struct_type].get_linked() is not None 35 | 36 | def get_source_code(self, transpiler): 37 | code = '' 38 | 39 | for input in self.inputs: 40 | initialization = input.get_source_initialization() 41 | if input.is_struct_member(): 42 | if initialization: 43 | code += transpiler.asignment(input.get_source_reference(), initialization) 44 | else: 45 | code += transpiler.declaration(input.data_type, 0, self.get_source_name(), initialization) 46 | 47 | return code 48 | 49 | 50 | classes = [ 51 | MaltStructNode, 52 | ] 53 | 54 | def register(): 55 | for _class in classes: bpy.utils.register_class(_class) 56 | 57 | def unregister(): 58 | for _class in reversed(classes): bpy.utils.unregister_class(_class) 59 | -------------------------------------------------------------------------------- /BlenderMalt/MaltNodes/_init_.py: -------------------------------------------------------------------------------- 1 | def get_modules(): 2 | from . import MaltNode, MaltNodeUITools, MaltNodeTree, MaltSocket, MaltCustomPasses 3 | modules = [MaltNode, MaltNodeUITools,MaltNodeTree, MaltSocket, MaltCustomPasses] 4 | 5 | import importlib, os 6 | nodes_dir = os.path.join(os.path.dirname(__file__), 'Nodes') 7 | for name in os.listdir(nodes_dir): 8 | try: 9 | if name == '__pycache__': 10 | continue 11 | if name.endswith('.py'): 12 | name = name[:-3] 13 | module = importlib.import_module(f'BlenderMalt.MaltNodes.Nodes.{name}') 14 | modules.append(module) 15 | except ModuleNotFoundError: 16 | # Ignore it. The file or dir is not a python module 17 | pass 18 | except Exception: 19 | import traceback 20 | traceback.print_exc() 21 | 22 | return modules 23 | 24 | 25 | def register(): 26 | import importlib 27 | for module in get_modules(): 28 | importlib.reload(module) 29 | 30 | for module in get_modules(): 31 | module.register() 32 | 33 | def unregister(): 34 | for module in reversed(get_modules()): 35 | module.unregister() 36 | -------------------------------------------------------------------------------- /BlenderMalt/MaltTextures.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from . import MaltPipeline 3 | 4 | __TEXTURES = {} 5 | 6 | def get_texture(texture): 7 | name = texture.name_full 8 | if name not in __TEXTURES or __TEXTURES[name] is None: 9 | __TEXTURES[name] = __load_texture(texture) 10 | return __TEXTURES[name] 11 | 12 | 13 | def __load_texture(texture): 14 | w,h = texture.size 15 | channels = int(texture.channels) 16 | size = w*h*channels 17 | sRGB = texture.colorspace_settings.name == 'sRGB' and texture.is_float == False 18 | if size == 0: 19 | return False 20 | 21 | buffer = MaltPipeline.get_bridge().get_shared_buffer(ctypes.c_float, size) 22 | texture.pixels.foreach_get(buffer.as_np_array()) 23 | 24 | MaltPipeline.get_bridge().load_texture(texture.name_full, buffer, (w,h), channels, sRGB) 25 | 26 | from Bridge.Proxys import TextureProxy 27 | return TextureProxy(texture.name_full) 28 | 29 | __GRADIENTS = {} 30 | __GRADIENT_RESOLUTION = 256 31 | # Blender doesn't trigger depsgraph updates for newly created textures, 32 | # so we always reload gradients until it's been succesfully unloaded once (TODO) 33 | __GRADIENTS_WORKAROUND = [] 34 | def add_gradient_workaround(texture): 35 | __GRADIENTS_WORKAROUND.append(texture.name_full) 36 | 37 | def get_gradient(texture): 38 | name = texture.name_full 39 | if name not in __GRADIENTS or __GRADIENTS[name] is None or name in __GRADIENTS_WORKAROUND: 40 | __GRADIENTS[name] = __load_gradient(texture) 41 | return __GRADIENTS[name] 42 | 43 | def __load_gradient(texture): 44 | pixels = [] 45 | for i in range(0, __GRADIENT_RESOLUTION): 46 | pixel = texture.color_ramp.evaluate( i*(1.0 / __GRADIENT_RESOLUTION)) 47 | pixels.extend(pixel) 48 | nearest = texture.color_ramp.interpolation == 'CONSTANT' 49 | MaltPipeline.get_bridge().load_gradient(texture.name_full, pixels, nearest) 50 | from Bridge.Proxys import GradientProxy 51 | return GradientProxy(texture.name_full) 52 | 53 | def copy_color_ramp(old, new): 54 | new.color_mode = old.color_mode 55 | new.hue_interpolation = old.hue_interpolation 56 | new.interpolation = old.interpolation 57 | while len(new.elements) > len(old.elements): 58 | new.elements.remove(new.elements[len(new.elements)-1]) 59 | for i, o in enumerate(old.elements): 60 | n = new.elements[i] if i < len(new.elements) else new.elements.new(o.position) 61 | n.position = o.position 62 | n.color = o.color[:] 63 | n.alpha = o.alpha 64 | 65 | def reset_textures(): 66 | global __TEXTURES 67 | __TEXTURES = {} 68 | global __GRADIENTS 69 | __GRADIENTS = {} 70 | 71 | def unload_texture(texture): 72 | __TEXTURES[texture.name_full] = None 73 | 74 | def unload_gradients(texture): 75 | __GRADIENTS[texture.name_full] = None 76 | if texture.name_full in __GRADIENTS_WORKAROUND: 77 | __GRADIENTS_WORKAROUND.remove(texture.name_full) 78 | 79 | def register(): 80 | pass 81 | 82 | def unregister(): 83 | pass 84 | -------------------------------------------------------------------------------- /BlenderMalt/readme.md: -------------------------------------------------------------------------------- 1 | # BlenderMalt 2 | 3 | ## Introduction 4 | 5 | *BlenderMalt* is a [*Malt Host*](../Malt#malt-pipelines) for *Blender*. 6 | It handles all the *Scene* data loading and syncronization and exposes a minimal UI suitable for a code-centric workflow. 7 | *BlenderMalt* communicates with *Malt* through a [*Bridge*](../Bridge) instance. 8 | 9 | ## RenderEngine 10 | 11 | [*MaltRenderEngine.py*](MaltRenderEngine.py) implements the *Blender* [*RenderEngine*](https://docs.blender.org/api/current/bpy.types.RenderEngine.html) interface. 12 | 13 | It takes care of generating a *Malt Scene* from the *Blender* [*DepsGraph*](https://docs.blender.org/api/current/bpy.types.Depsgraph.html), send it to *Bridge* for rendering and pass the result back to *Blender*. 14 | 15 | ## Pipeline 16 | 17 | [*MaltPipeline.py*](MaltPipeline.py) handles *Bridge* instances, makes sure all *Blender* objects have their respective *Pipeline Parameters* registered as *MaltPropertyGroups*, and handles *Meshes* and *Textures* updates. 18 | 19 | ## Pipeline Properties 20 | 21 | [*MaltPropertyGroups*](MaltProperties.py) store *Malt* *Pipeline Parameters* as native *Blender* [*PropertyGroups*](https://docs.blender.org/api/current/bpy.types.PropertyGroup.html) and convert them to *Malt* *Scene* parameters on request. 22 | They also can handle their own UI rendering automatically, all that is needed is to pass a *Blender* [*UI Layout*](https://docs.blender.org/api/current/bpy.types.UILayout.html) to their *draw_ui* method. 23 | 24 | ## Materials 25 | 26 | [*MaltMaterial.py*](MaltMaterial.py) handles the compilation (including automatic hot-reloading), UI rendering and storage as native *Blender* materials of *Malt* *Pipeline* materials. 27 | 28 | ## Meshes 29 | 30 | [*MaltMeshes.py*](MaltMeshes.py) retrieves any *Blender* geometry as vertex and index buffers and sends it to *Bridge*. 31 | This module is highly optimized to allow real-time mesh editing and (when possible) retrieves vertex data directly from *Blender* internal C data through the [*CBlenderMalt*](CBlenderMalt) library. 32 | 33 | ## Textures 34 | 35 | [*MaltTextures.py*](MaltTextures.py) sends 1D and 2D textures pixel data to *Bridge*. 36 | 37 | -------------------------------------------------------------------------------- /Bridge/Material.py: -------------------------------------------------------------------------------- 1 | from Malt.PipelineParameters import Parameter 2 | 3 | MATERIAL_SHADERS = {} 4 | 5 | class Material(): 6 | 7 | def __init__(self, path, pipeline, search_paths=[], custom_passes={}): 8 | self.path = path 9 | self.parameters = {} 10 | self.compiler_error = '' 11 | 12 | compiled_material = pipeline.compile_material(path, search_paths)#, custom_passes) 13 | 14 | if isinstance(compiled_material, str): 15 | self.compiler_error = compiled_material 16 | else: 17 | for pass_name, shader in compiled_material.items(): 18 | for uniform_name, uniform in shader.uniforms.items(): 19 | self.parameters[uniform_name] = Parameter.from_uniform(uniform) 20 | if shader.error: 21 | self.compiler_error += pass_name + " : " + shader.error 22 | if shader.validator: 23 | self.compiler_error += pass_name + " : " + shader.validator 24 | 25 | if self.compiler_error == '': 26 | global MATERIAL_SHADERS 27 | MATERIAL_SHADERS[self.path] = compiled_material 28 | else: 29 | MATERIAL_SHADERS[self.path] = {} 30 | 31 | 32 | def get_shader(path, parameters): 33 | if path not in MATERIAL_SHADERS.keys(): 34 | return {} 35 | shaders = MATERIAL_SHADERS[path] 36 | new_shader = {} 37 | 38 | for pass_name, pass_shader in shaders.items(): 39 | if pass_shader.error: 40 | new_shader = {} 41 | break 42 | 43 | pass_shader_copy = pass_shader.copy() 44 | new_shader[pass_name] = pass_shader_copy 45 | 46 | for name, parameter in parameters.items(): 47 | if name in pass_shader_copy.textures.keys(): 48 | pass_shader_copy.textures[name] = parameter 49 | elif name in pass_shader_copy.uniforms.keys(): 50 | pass_shader_copy.uniforms[name].set_value(parameter) 51 | 52 | return new_shader 53 | -------------------------------------------------------------------------------- /Bridge/Mesh.py: -------------------------------------------------------------------------------- 1 | MESHES = {} 2 | 3 | def load_mesh(pipeline, msg): 4 | name = msg['name'] 5 | data = msg['data'] 6 | 7 | MESHES[name] = pipeline.load_mesh( 8 | position = data['positions'], 9 | indices = data['indices'], 10 | normal = data['normals'], 11 | tangent = data['tangents'], 12 | uvs = data['uvs'], 13 | colors = data['colors'] 14 | ) 15 | -------------------------------------------------------------------------------- /Bridge/Proxys.py: -------------------------------------------------------------------------------- 1 | from Malt.GL.Mesh import Mesh 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.Texture import Gradient 4 | from Malt.Scene import Material 5 | 6 | class MeshProxy(Mesh): 7 | 8 | def __init__(self, name, submesh_index): 9 | self.name = name 10 | self.mesh = None 11 | self.submesh_index = submesh_index 12 | 13 | def resolve(self): 14 | import Bridge.Mesh 15 | self.mesh = Bridge.Mesh.MESHES[self.name][self.submesh_index] 16 | self.__dict__.update(self.mesh.__dict__) 17 | 18 | def __del__(self): 19 | pass 20 | 21 | class TextureProxy(Texture): 22 | 23 | def __init__(self, name): 24 | self.name = name 25 | self.texture = None 26 | 27 | def resolve(self): 28 | import Bridge.Texture 29 | self.texture = Bridge.Texture.TEXTURES[self.name] 30 | self.__dict__.update(self.texture.__dict__) 31 | 32 | def __del__(self): 33 | pass 34 | 35 | class GradientProxy(Gradient): 36 | 37 | def __init__(self, name): 38 | self.name = name 39 | self.gradient = None 40 | 41 | def resolve(self): 42 | import Bridge.Texture 43 | self.gradient = Bridge.Texture.GRADIENTS[self.name] 44 | self.__dict__.update(self.gradient.__dict__) 45 | 46 | def __del__(self): 47 | pass 48 | 49 | class MaterialProxy(Material): 50 | 51 | def __init__(self, path, shader_parameters, parameters): 52 | self.path = path 53 | self.shader_parameters = shader_parameters 54 | super().__init__(None, parameters) 55 | 56 | def resolve(self): 57 | import Bridge.Material 58 | self.shader = Bridge.Material.get_shader(self.path, self.shader_parameters) 59 | -------------------------------------------------------------------------------- /Bridge/Texture.py: -------------------------------------------------------------------------------- 1 | from Malt.GL import Texture 2 | from Malt.GL.GL import * 3 | 4 | TEXTURES = {} 5 | 6 | def load_texture(msg): 7 | name = msg['name'] 8 | data = msg['buffer'].buffer() 9 | resolution = msg['resolution'] 10 | channels = msg['channels'] 11 | sRGB = msg['sRGB'] 12 | 13 | internal_formats = [ 14 | GL_R32F, 15 | GL_RG32F, 16 | GL_RGB32F, 17 | GL_RGBA32F, 18 | ] 19 | pixel_formats = [ 20 | GL_RED, 21 | GL_RG, 22 | GL_RGB, 23 | GL_RGBA 24 | ] 25 | internal_format = internal_formats[channels-1] 26 | pixel_format = pixel_formats[channels-1] 27 | 28 | if sRGB: 29 | if channels == 4: 30 | internal_format = GL_SRGB_ALPHA 31 | else: 32 | internal_format = GL_SRGB 33 | 34 | #Nearest + Anisotropy seems to yield the best results with temporal super sampling 35 | TEXTURES[name] = Texture.Texture(resolution, internal_format, GL_FLOAT, data, pixel_format=pixel_format, 36 | wrap=GL_REPEAT, min_filter=GL_NEAREST_MIPMAP_NEAREST, build_mipmaps=True, anisotropy=True) 37 | 38 | GRADIENTS = {} 39 | 40 | def load_gradient(name, pixels, nearest): 41 | GRADIENTS[name] = Texture.Gradient(pixels, len(pixels)/4, nearest_interpolation=nearest) 42 | -------------------------------------------------------------------------------- /Bridge/__init__.py: -------------------------------------------------------------------------------- 1 | def reload(): 2 | import importlib 3 | from . import Client_API, Server, Material, Mesh, Texture 4 | for module in [ Client_API, Server, Material, Mesh, Texture ]: 5 | importlib.reload(module) 6 | 7 | def start_server(pipeline_path, viewport_bit_depth, connection_addresses, 8 | shared_dic, lock, log_path, debug_mode, renderdoc_path, plugins_paths, docs_path): 9 | import logging 10 | log_level = logging.DEBUG if debug_mode else logging.INFO 11 | logging.basicConfig(filename=log_path, level=log_level, format='Malt > %(message)s') 12 | console_logger = logging.StreamHandler() 13 | console_logger.setLevel(logging.WARNING) 14 | logging.getLogger().addHandler(console_logger) 15 | 16 | import os, sys, ctypes 17 | if sys.platform == 'win32': 18 | win = ctypes.windll.kernel32 19 | HIGH_PRIORITY_CLASS = 0x00000080 20 | PROCESS_SET_INFORMATION = 0x0200 21 | process = win.OpenProcess(PROCESS_SET_INFORMATION, 0, win.GetCurrentProcessId()) 22 | win.SetPriorityClass(process, HIGH_PRIORITY_CLASS) 23 | win.CloseHandle(process) 24 | if renderdoc_path and os.path.exists(renderdoc_path): 25 | import subprocess 26 | subprocess.call([renderdoc_path, 'inject', '--PID={}'.format(os.getpid())]) 27 | 28 | from . import Server 29 | try: 30 | Server.main(pipeline_path, viewport_bit_depth, connection_addresses, 31 | shared_dic, lock, log_path, debug_mode, plugins_paths, docs_path) 32 | except: 33 | import traceback 34 | logging.error(traceback.format_exc()) 35 | -------------------------------------------------------------------------------- /Bridge/ipc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | # set(CMAKE_GENERATOR_PLATFORM x64) 4 | 5 | project(Ipc) 6 | 7 | SET(CMAKE_BUILD_TYPE Release) 8 | SET(BUILD_SHARED_LIBS ON) 9 | 10 | add_library(Ipc ipc.c) 11 | 12 | if(NOT WIN32) 13 | target_link_libraries(Ipc pthread) 14 | if(NOT APPLE) 15 | target_link_libraries(Ipc rt) 16 | endif() 17 | endif() 18 | 19 | install(TARGETS Ipc CONFIGURATIONS Release DESTINATION ${PROJECT_SOURCE_DIR}) 20 | 21 | -------------------------------------------------------------------------------- /Bridge/ipc/build.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import platform 4 | 5 | src_dir = os.path.abspath(os.path.dirname(__file__)) 6 | build_dir = os.path.join(src_dir, '.build') 7 | 8 | try: os.mkdir(build_dir) 9 | except: pass 10 | 11 | if platform.system() == 'Windows': #Multi-config generators, like Visual Studio 12 | subprocess.check_call(['cmake', '-A', 'x64', '..'], cwd=build_dir) 13 | subprocess.check_call(['cmake', '--build', '.', '--config', 'Release'], cwd=build_dir) 14 | else: #Single-config generators 15 | subprocess.check_call(['cmake', '..'], cwd=build_dir) 16 | subprocess.check_call(['cmake', '--build', '.'], cwd=build_dir) 17 | 18 | subprocess.check_call(['cmake', '--install', '.'], cwd=build_dir) 19 | -------------------------------------------------------------------------------- /Bridge/ipc/ipc.c: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #define EXPORT __declspec( dllexport ) 3 | #else 4 | #define EXPORT __attribute__ ((visibility ("default"))) 5 | #endif 6 | 7 | #define IPC_IMPLEMENTATION 8 | #include "ipc.h" 9 | 10 | EXPORT int create_shared_memory(char* name, size_t size, ipc_sharedmemory* mem) 11 | { 12 | ipc_mem_init(mem, name, size); 13 | if (ipc_mem_create(mem) != 0) { 14 | return errno; 15 | } 16 | return 0; 17 | } 18 | 19 | EXPORT int open_shared_memory(char* name, size_t size, ipc_sharedmemory* mem) 20 | { 21 | ipc_mem_init(mem, name, size); 22 | if (ipc_mem_open_existing(mem) != 0) { 23 | return errno; 24 | } 25 | return 0; 26 | } 27 | 28 | EXPORT void close_shared_memory(ipc_sharedmemory mem, bool release) 29 | { 30 | ipc_mem_close(&mem, release); 31 | } 32 | -------------------------------------------------------------------------------- /Bridge/renderdoc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | # set(CMAKE_GENERATOR_PLATFORM x64) 4 | 5 | project(RenderDocWrapper) 6 | 7 | SET(CMAKE_BUILD_TYPE Release) 8 | SET(BUILD_SHARED_LIBS ON) 9 | 10 | add_library(renderdoc_wrapper renderdoc_wrapper.c) 11 | 12 | if(NOT WIN32) 13 | target_link_libraries(renderdoc_wrapper dl) 14 | endif() 15 | 16 | install(TARGETS renderdoc_wrapper CONFIGURATIONS Release DESTINATION ${PROJECT_SOURCE_DIR}) 17 | 18 | -------------------------------------------------------------------------------- /Bridge/renderdoc/__init__.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import ctypes 4 | 5 | import platform 6 | 7 | src_dir = os.path.abspath(os.path.dirname(__file__)) 8 | 9 | library = 'librenderdoc_wrapper.so' 10 | if platform.system() == 'Windows': library = 'renderdoc_wrapper.dll' 11 | if platform.system() == 'Darwin': library = 'librenderdoc_wrapper.dylib' 12 | 13 | renderdoc = ctypes.CDLL(os.path.join(src_dir, library)) 14 | 15 | capture_start = renderdoc['capture_start'] 16 | capture_start.restype = None 17 | 18 | capture_end = renderdoc['capture_end'] 19 | capture_end.restype = None 20 | -------------------------------------------------------------------------------- /Bridge/renderdoc/build.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import platform 4 | 5 | src_dir = os.path.abspath(os.path.dirname(__file__)) 6 | build_dir = os.path.join(src_dir, '.build') 7 | 8 | try: os.mkdir(build_dir) 9 | except: pass 10 | 11 | if platform.system() == 'Windows': #Multi-config generators, like Visual Studio 12 | subprocess.check_call(['cmake', '-A', 'x64', '..'], cwd=build_dir) 13 | subprocess.check_call(['cmake', '--build', '.', '--config', 'Release'], cwd=build_dir) 14 | else: #Single-config generators 15 | subprocess.check_call(['cmake', '..'], cwd=build_dir) 16 | subprocess.check_call(['cmake', '--build', '.'], cwd=build_dir) 17 | 18 | subprocess.check_call(['cmake', '--install', '.'], cwd=build_dir) 19 | -------------------------------------------------------------------------------- /Bridge/renderdoc/renderdoc_wrapper.c: -------------------------------------------------------------------------------- 1 | #include "renderdoc_app.h" 2 | #include 3 | 4 | #ifdef _WIN32 5 | #define EXPORT __declspec( dllexport ) 6 | #else 7 | #define EXPORT __attribute__ ((visibility ("default"))) 8 | #endif 9 | 10 | RENDERDOC_API_1_4_1* API = NULL; 11 | 12 | #ifdef _WIN32 13 | #include 14 | void init() 15 | { 16 | if (API) return; 17 | HMODULE module = GetModuleHandleA("renderdoc.dll"); 18 | if (module) 19 | { 20 | pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(module, "RENDERDOC_GetAPI"); 21 | int result = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_1, (void **)&API); 22 | if (result != 1) API = NULL; 23 | } 24 | } 25 | #else 26 | #include 27 | void init() 28 | { 29 | if (API) return; 30 | void* module = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD); 31 | if (module) 32 | { 33 | pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(module, "RENDERDOC_GetAPI"); 34 | int result = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_1, (void **)&API); 35 | if (result != 1) API = NULL; 36 | } 37 | } 38 | #endif 39 | 40 | EXPORT void capture_start() 41 | { 42 | init(); 43 | if (API) API->StartFrameCapture(NULL, NULL); 44 | } 45 | 46 | EXPORT void capture_end() 47 | { 48 | init(); 49 | if (API) API->EndFrameCapture(NULL, NULL); 50 | } 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Malt - Copyright (c) 2020-2022 BNPR, Miguel Pozo and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE - DEPENDENCIES: -------------------------------------------------------------------------------- 1 | Malt: 2 | 3 | PyOpenGL 4 | BSD License 5 | http://pyopengl.sourceforge.net 6 | https://github.com/mcfletch/pyopengl/blob/master/license.txt 7 | 8 | pyGLFW 9 | MIT License 10 | https://github.com/FlorianRhiem/pyGLFW 11 | https://github.com/FlorianRhiem/pyGLFW/blob/master/LICENSE.txt 12 | 13 | GLFW 14 | zlib License 15 | https://www.glfw.org/ 16 | https://github.com/glfw/glfw/blob/master/LICENSE.md 17 | 18 | mcpp 19 | MIT License 20 | https://github.com/pragma37/mcpp 21 | https://github.com/pragma37/mcpp/blob/master/LICENSE 22 | 23 | multipledispatch 24 | BSD License 25 | http://github.com/mrocklin/multipledispatch/ 26 | https://github.com/mrocklin/multipledispatch/blob/master/LICENSE.txt 27 | 28 | numpy 29 | BSD License 30 | https://www.numpy.org 31 | https://github.com/numpy/numpy/blob/main/LICENSE.txt 32 | https://github.com/numpy/numpy/blob/main/LICENSES_bundled.txt 33 | 34 | pyrr 35 | BSD License 36 | https://github.com/adamlwgriffiths/Pyrr 37 | https://github.com/adamlwgriffiths/Pyrr/blob/master/LICENSE 38 | 39 | six 40 | MIT License 41 | https://github.com/benjaminp/six 42 | https://github.com/benjaminp/six/blob/master/LICENSE 43 | 44 | GLSLParser: 45 | 46 | PEGTL 47 | Boost Software License 48 | https://github.com/taocpp/PEGTL 49 | https://github.com/taocpp/PEGTL/blob/main/LICENSE_1_0.txt 50 | 51 | rapidjson 52 | MIT License 53 | https://github.com/Tencent/rapidjson 54 | https://github.com/Tencent/rapidjson/blob/master/license.txt 55 | 56 | Bridge: 57 | 58 | ipc 59 | The Unlicense 60 | https://github.com/jarikomppa/ipc 61 | https://github.com/jarikomppa/ipc/blob/master/LICENSE 62 | 63 | renderdoc (renderdoc_app.h) 64 | MIT License 65 | https://renderdoc.org/ 66 | https://github.com/baldurk/renderdoc/blob/v1.x/LICENSE.md 67 | 68 | BlenderMalt: 69 | 70 | blender 71 | GNU GPL 72 | https://blender.org 73 | https://github.com/blender/blender/blob/master/doc/license/GPL3-license.txt 74 | 75 | MikkTSpace 76 | zlib License 77 | http://mikktspace.com/ 78 | https://github.com/mmikk/MikkTSpace/blob/master/mikktspace.h 79 | -------------------------------------------------------------------------------- /LICENSE - DEPENDENCIES (FULL TEXT): -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/LICENSE - DEPENDENCIES (FULL TEXT) -------------------------------------------------------------------------------- /Malt/GL/GL.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | import OpenGL 4 | #OpenGL.ERROR_LOGGING = False 5 | #OpenGL.FULL_LOGGING = False 6 | #OpenGL.ERROR_ON_COPY = False 7 | from OpenGL.GL import * 8 | from OpenGL.extensions import hasGLExtension 9 | 10 | if True: 11 | #For some reason PyOpenGL doesnt support the most common depth/stencil buffer by default ??? 12 | #https://sourceforge.net/p/pyopengl/bugs/223/ 13 | from OpenGL import images 14 | images.TYPE_TO_ARRAYTYPE[GL_UNSIGNED_INT_24_8] = GL_UNSIGNED_INT 15 | images.TIGHT_PACK_FORMATS[GL_UNSIGNED_INT_24_8] = 4 16 | images.TYPE_TO_ARRAYTYPE[GL_HALF_FLOAT] = GL_HALF_FLOAT 17 | from OpenGL import arrays 18 | if arrays.ADT: 19 | arrays.GL_CONSTANT_TO_ARRAY_TYPE[GL_HALF_FLOAT] = arrays.ADT(GL_HALF_FLOAT, GLhalfARB) 20 | else: 21 | class GLhalfFloatArray(ArrayDatatype, ctypes.POINTER(GLhalfARB)): 22 | baseType = GLhalfARB 23 | typeConstant = GL_HALF_FLOAT 24 | arrays.GL_CONSTANT_TO_ARRAY_TYPE[GL_HALF_FLOAT] = GLhalfFloatArray 25 | 26 | NULL = None 27 | GL_ENUMS = {} 28 | GL_NAMES = {} 29 | 30 | if True: #create new scope to import OpenGL 31 | from OpenGL import GL 32 | for e in dir(GL): 33 | if e.startswith('GL_'): 34 | GL_ENUMS[getattr(GL, e)] = e 35 | GL_NAMES[e] = getattr(GL, e) 36 | 37 | class DrawQuery(): 38 | 39 | def __init__(self, query_type=GL_ANY_SAMPLES_PASSED): 40 | self.query = None 41 | self.query_type = query_type 42 | 43 | def begin_query(self): 44 | if self.query: 45 | glDeleteQueries(1, self.query) 46 | self.query = gl_buffer(GL_UNSIGNED_INT, 1) 47 | glGenQueries(1, self.query) 48 | glBeginQuery(self.query_type, self.query[0]) 49 | 50 | def end_query(self): 51 | glEndQuery(self.query_type) 52 | 53 | def begin_conditional_draw(self, wait_mode=GL_QUERY_WAIT): 54 | glBeginConditionalRender(self.query[0], wait_mode) 55 | 56 | def end_conditional_draw(self): 57 | glEndConditionalRender() 58 | 59 | 60 | def gl_buffer(type, size, data=None): 61 | types = { 62 | GL_BYTE : GLbyte, 63 | GL_UNSIGNED_BYTE : GLubyte, 64 | GL_SHORT : GLshort, 65 | GL_UNSIGNED_SHORT : GLushort, 66 | GL_INT : GLint, 67 | GL_UNSIGNED_INT : GLuint, 68 | #GL_HALF_FLOAT : GLhalfARB, 69 | GL_HALF_FLOAT : GLfloat, 70 | GL_FLOAT : GLfloat, 71 | GL_DOUBLE : GLdouble, 72 | GL_BOOL : GLboolean, 73 | } 74 | gl_type = (types[type] * size) 75 | if data: 76 | try: 77 | return gl_type(*data) 78 | except: 79 | return gl_type(data) 80 | else: 81 | return gl_type() 82 | 83 | 84 | def buffer_to_string(buffer): 85 | chars = [] 86 | for char in list(buffer): 87 | if chr(char) == '\0': 88 | break 89 | chars.append(chr(char)) 90 | return ''.join(chars) 91 | -------------------------------------------------------------------------------- /Malt/GL/GLSLEval.py: -------------------------------------------------------------------------------- 1 | def glsl_vector(convert, length, *args): 2 | unpacked_args = [] 3 | for arg in args: 4 | try: 5 | unpacked_args.extend([*arg]) 6 | except: 7 | unpacked_args.append(arg) 8 | unpacked_args = [convert(arg) for arg in unpacked_args] 9 | if len(unpacked_args) == 0: 10 | return (0.0)*length 11 | elif len(unpacked_args) == 1: 12 | return (unpacked_args[0],) * length 13 | else: 14 | assert(len(unpacked_args) == length) 15 | return tuple(unpacked_args) 16 | 17 | def _vec2(convert, *args): return glsl_vector(convert, 2, *args) 18 | def _vec3(convert, *args): return glsl_vector(convert, 3, *args) 19 | def _vec4(convert, *args): return glsl_vector(convert, 4, *args) 20 | 21 | def vec2(*args): return _vec2(float, *args) 22 | def vec3(*args): return _vec3(float, *args) 23 | def vec4(*args): return _vec4(float, *args) 24 | 25 | def ivec2(*args): return _vec2(int, *args) 26 | def ivec3(*args): return _vec3(int, *args) 27 | def ivec4(*args): return _vec4(int, *args) 28 | 29 | def uint(n): return max(int(n), 0) 30 | 31 | def uvec2(*args): return _vec2(uint, *args) 32 | def uvec3(*args): return _vec3(uint, *args) 33 | def uvec4(*args): return _vec4(uint, *args) 34 | 35 | def glsl_eval(str): 36 | true = True 37 | false = False 38 | return eval(str) 39 | -------------------------------------------------------------------------------- /Malt/GL/GLSLParser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(GLSLParser) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | add_subdirectory(external) 6 | 7 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.bin") 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 9 | 10 | file(GLOB_RECURSE src_glob "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") 11 | 12 | add_executable(${PROJECT_NAME} ${src_glob}) 13 | 14 | target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") 15 | 16 | #target_precompile_headers(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/pch.h) 17 | 18 | target_link_libraries(${PROJECT_NAME} pegtl) 19 | target_include_directories(${PROJECT_NAME} PUBLIC ${RAPIDJSON_INCLUDE_DIR}) 20 | 21 | -------------------------------------------------------------------------------- /Malt/GL/GLSLParser/build.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import platform 4 | 5 | current_dir = os.path.abspath(os.path.dirname(__file__)) 6 | build_dir = os.path.join(current_dir, '.build') 7 | 8 | try: os.mkdir(build_dir) 9 | except: pass 10 | 11 | if platform.system() == 'Windows': #Multi-config generators, like Visual Studio 12 | subprocess.check_call(['cmake', '-A', 'x64', '..'], cwd=build_dir) 13 | subprocess.check_call(['cmake', '--build', '.', '--config', 'Release'], cwd=build_dir) 14 | else: #Single-config generators 15 | subprocess.check_call(['cmake', '..'], cwd=build_dir) 16 | subprocess.check_call(['cmake', '--build', '.'], cwd=build_dir) 17 | 18 | #subprocess.check_call(['cmake', '--install', '.', '--prefix', '.'], cwd=build_dir) 19 | -------------------------------------------------------------------------------- /Malt/GL/GLSLParser/external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | include(ExternalProject) 3 | 4 | FetchContent_Declare( 5 | pegtl 6 | GIT_REPOSITORY https://github.com/taocpp/PEGTL 7 | GIT_TAG 3.2.1 8 | ) 9 | FetchContent_MakeAvailable(pegtl) 10 | 11 | 12 | FetchContent_Declare( 13 | rapidjson 14 | GIT_REPOSITORY https://github.com/Tencent/rapidjson 15 | GIT_TAG 00dbcf2c6e03c47d6c399338b6de060c71356464 16 | ) 17 | set(RAPIDJSON_BUILD_DOC OFF CACHE INTERNAL "") 18 | set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE INTERNAL "") 19 | set(RAPIDJSON_BUILD_TESTS OFF CACHE INTERNAL "") 20 | FetchContent_MakeAvailable(rapidjson) 21 | FetchContent_GetProperties(rapidjson) 22 | set(RAPIDJSON_INCLUDE_DIR ${rapidjson_SOURCE_DIR}/include PARENT_SCOPE) 23 | 24 | #[[ 25 | ExternalProject_Add( 26 | rapidjson 27 | PREFIX ${CMAKE_BINARY_DIR}/rapidjson 28 | GIT_REPOSITORY https://github.com/Tencent/rapidjson 29 | GIT_TAG 00dbcf2c6e03c47d6c399338b6de060c71356464 30 | CONFIGURE_COMMAND "" 31 | BUILD_COMMAND "" 32 | INSTALL_COMMAND "" 33 | LOG_DOWNLOAD ON 34 | ) 35 | ExternalProject_Get_Property(rapidjson SOURCE_DIR) 36 | set(RAPIDJSON_INCLUDE_DIR ${SOURCE_DIR}/include PARENT_SCOPE) 37 | ]] 38 | 39 | -------------------------------------------------------------------------------- /Malt/GL/GLSLParser/external/PEGTL-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2007-2021 Dr. Colin Hirsch and Daniel Frey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Malt/GL/readme.md: -------------------------------------------------------------------------------- 1 | # GL 2 | 3 | The GL folder contains a series of modules that abstracts commonly needed OpenGL functionality. 4 | It doesn't try to be a complete abstraction, so it's meant to be used alongside raw OpenGL calls. 5 | 6 | ## [GL.py](GL.py) 7 | Loads OpenGL functions via [PyOpenGL](https://pypi.org/project/PyOpenGL/), provides OpenGL enums reflection dictionaries and implements a Python to OpenGL types conversion function via the *gl_buffer function*. 8 | 9 | ## [Mesh.py](Mesh.py) 10 | 11 | Builds VAOs with the same layout used by [Common.glsl](Malt/Shaders/Common.glsl). 12 | Each parameter expects a flat 1D iterable with all the per-vertex data. 13 | Tangents, UVs and Vertex Colors buffers must be passed inside a list, even if you pass only one. Four of each are allowed at most. 14 | 15 | The MeshCustomLoad class doesn't do any loading by itself. It's reserved for cases where special opimizations are needed, like [BlenderMalt/MaltMeshes.py](BlenderMalt/MaltMeshes.py). 16 | 17 | * [OpenGL Wiki - Vertex Array Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object) 18 | * [LearnOpenGL - Hello Triangle](https://learnopengl.com/Getting-started/Hello-Triangle) 19 | 20 | ## [Shader.py](Shader.py) 21 | 22 | Builds OpenGL programs from GLSL vertex and fragment shader source code and provides an interface for reflection and configuration of shader parameters. 23 | 24 | The *shader_preprocessor* function parses source code with a C preprocessor to provide support for *#include directives* in glsl shaders. 25 | 26 | * [OpenGL Wiki - GLSL Objects](https://www.khronos.org/opengl/wiki/GLSL_Object) 27 | * [Learn OpenGL - Shaders](https://learnopengl.com/Getting-started/Shaders) 28 | 29 | ## [Texture.py](Texture.py) 30 | 31 | Loads 1D textures (*Gradient*), 2D textures (*Texture*), 2D texture arrays (*TextureArray*), cube maps (*CubeMap*) and cube map arrays (*CubeMapArrays*) under a simple and mostly shared interface. 32 | The *internal_format* parameter is the [OpenGL image format](https://www.khronos.org/opengl/wiki/Image_Format) the texture will be stored inside the GPU. 33 | 34 | * [OpenGL Wiki - Texture](https://www.khronos.org/opengl/wiki/Texture) 35 | * [Learn OpenGL - Textures](https://learnopengl.com/Getting-started/Textures) 36 | 37 | ## [RenderTarget.py](RenderTarget.py) 38 | 39 | Builds FBOs from Textures. It accepts an arbitrary number of color targets and a single depth/stencil target. 40 | Color target Textures must be passed inside a list, even if you pass only one. 41 | 42 | For rendering to other types of targets (like Cube Maps and Texture Arrays) you can pass an object with a custom attach method (See the *ArrayLayerTarget* class for an example). 43 | 44 | * [OpenGL Wiki - Framebuffer Objects](https://www.khronos.org/opengl/wiki/Framebuffer_Object) 45 | * [Learn OpenGL - Framebuffers](https://learnopengl.com/Advanced-OpenGL/Framebuffers) 46 | 47 | ## Basic Example (draw a full screen quad) 48 | 49 | ```python 50 | positions=[ 51 | 1.0, 1.0, 0.0, 52 | 1.0, -1.0, 0.0, 53 | -1.0, -1.0, 0.0, 54 | -1.0, 1.0, 0.0, 55 | ] 56 | indices=[ 57 | 0, 1, 3, 58 | 1, 2, 3, 59 | ] 60 | 61 | mesh = Mesh(positions, indices) 62 | 63 | vertex_source=''' 64 | layout (location = 0) in vec3 POSITION; 65 | void main() 66 | { 67 | gl_Position = vec4(POSITION, 1); 68 | } 69 | ''' 70 | 71 | pixel_source=''' 72 | layout (location = 0) out vec4 RESULT; 73 | uniform vec4 color = vec4(1,0,0,1); 74 | void main() 75 | { 76 | RESULT = color; 77 | } 78 | ''' 79 | 80 | shader = Shader(vertex_source, pixel_source) 81 | 82 | shader.uniforms['color'].set_value((0,1,0,1)) 83 | 84 | result_texture = Texture((1024, 1024), GL_RGBA32F) 85 | 86 | result_target = RenderTarget([result_texture]) 87 | 88 | result_target.bind() 89 | shader.bind() 90 | mesh.draw() 91 | ``` 92 | -------------------------------------------------------------------------------- /Malt/Nodes/LineRender.py: -------------------------------------------------------------------------------- 1 | from Malt.GL.GL import * 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.RenderTarget import RenderTarget 4 | from Malt.PipelineNode import PipelineNode 5 | from Malt.PipelineParameters import Parameter, Type 6 | 7 | _SHADER = None 8 | 9 | class LineRender(PipelineNode): 10 | """ 11 | Expands the line up to the width especified in the *Line Width* texture 12 | and composites it on top of the *Color* texture. 13 | """ 14 | 15 | def __init__(self, pipeline): 16 | PipelineNode.__init__(self, pipeline) 17 | self.resolution = None 18 | 19 | @classmethod 20 | def reflect_inputs(cls): 21 | inputs = {} 22 | inputs['Color'] = Parameter('', Type.TEXTURE) 23 | inputs['Line Color'] = Parameter('', Type.TEXTURE) 24 | inputs['Line Width'] = Parameter('', Type.TEXTURE) 25 | inputs['Max Width'] = Parameter(10, Type.INT, doc=""" 26 | The maximum width the shader can render. 27 | Increasing the value lowers the render performance.""") 28 | inputs['Line Scale'] = Parameter(1.0, Type.FLOAT, doc=""" 29 | Scale all Line Width values with this one. 30 | *(Useful for rendering at different resolutions)*""") 31 | inputs['Normal Depth'] = Parameter('', Type.TEXTURE) 32 | inputs['ID'] = Parameter('', Type.TEXTURE) 33 | return inputs 34 | 35 | @classmethod 36 | def reflect_outputs(cls): 37 | outputs = {} 38 | outputs['Color'] = Parameter('', Type.TEXTURE) 39 | return outputs 40 | 41 | def setup_render_targets(self, resolution): 42 | self.t_color = Texture(resolution, GL_RGBA16F) 43 | self.fbo_color = RenderTarget([self.t_color]) 44 | 45 | def execute(self, parameters): 46 | inputs = parameters['IN'] 47 | outputs = parameters['OUT'] 48 | 49 | if self.pipeline.resolution != self.resolution: 50 | self.setup_render_targets(self.pipeline.resolution) 51 | self.resolution = self.pipeline.resolution 52 | 53 | global _SHADER 54 | if _SHADER is None: 55 | _SHADER = self.pipeline.compile_shader_from_source('#include "Passes/LineComposite.glsl"') 56 | 57 | _SHADER.textures['color_texture'] = inputs['Color'] 58 | _SHADER.textures['depth_texture'] = inputs['Normal Depth'] 59 | _SHADER.uniforms['depth_channel'].set_value(3) 60 | _SHADER.textures['id_texture'] = inputs['ID'] 61 | _SHADER.textures['line_color_texture'] = inputs['Line Color'] 62 | _SHADER.textures['line_width_texture'] = inputs['Line Width'] 63 | _SHADER.uniforms['line_width_scale'].set_value(inputs['Line Scale']) 64 | _SHADER.uniforms['brute_force_range'].set_value(inputs['Max Width']) 65 | 66 | self.pipeline.common_buffer.shader_callback(_SHADER) 67 | self.pipeline.draw_screen_pass(_SHADER, self.fbo_color) 68 | 69 | outputs['Color'] = self.t_color 70 | 71 | NODE = LineRender 72 | -------------------------------------------------------------------------------- /Malt/Nodes/SceneFilter.py: -------------------------------------------------------------------------------- 1 | from Malt.GL.GL import * 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.RenderTarget import RenderTarget 4 | from Malt.PipelineNode import PipelineNode 5 | from Malt.PipelineParameters import Parameter, Type 6 | from copy import copy, deepcopy 7 | 8 | class SceneFilter(PipelineNode): 9 | """ 10 | Filters a Scene based on object tags. 11 | """ 12 | 13 | def __init__(self, pipeline): 14 | PipelineNode.__init__(self, pipeline) 15 | self.last_scene = None 16 | self.matches = None 17 | self.non_matches = None 18 | 19 | @classmethod 20 | def reflect_inputs(cls): 21 | return { 22 | 'Scene' : Parameter('Scene', Type.OTHER), 23 | 'Filter' : Parameter('', Type.STRING) 24 | } 25 | 26 | @classmethod 27 | def reflect_outputs(cls): 28 | return { 29 | 'Matches' : Parameter('Scene', Type.OTHER), 30 | 'Non Matches' : Parameter('Scene', Type.OTHER) 31 | } 32 | 33 | def execute(self, parameters): 34 | inputs = parameters['IN'] 35 | outputs = parameters['OUT'] 36 | 37 | scene = inputs['Scene'] 38 | tag = inputs['Filter'] 39 | 40 | if scene != self.last_scene: 41 | self.last_scene = scene 42 | self.matches = copy(scene) 43 | self.matches.objects = [] 44 | self.matches.shader_resources = copy(self.matches.shader_resources) 45 | self.non_matches = copy(scene) 46 | self.non_matches.objects = [] 47 | self.non_matches.shader_resources = copy(self.non_matches.shader_resources) 48 | for obj in scene.objects: 49 | if tag in obj.tags: 50 | self.matches.objects.append(obj) 51 | else: 52 | self.non_matches.objects.append(obj) 53 | self.matches.batches = self.pipeline.build_scene_batches(self.matches.objects) 54 | self.non_matches.batches = self.pipeline.build_scene_batches(self.non_matches.objects) 55 | 56 | outputs['Matches'] = self.matches 57 | outputs['Non Matches'] = self.non_matches 58 | 59 | 60 | NODE = SceneFilter 61 | -------------------------------------------------------------------------------- /Malt/Nodes/SuperSamplingAA.py: -------------------------------------------------------------------------------- 1 | from Malt.GL.GL import * 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.RenderTarget import RenderTarget 4 | from Malt.PipelineNode import PipelineNode 5 | from Malt.PipelineParameters import Parameter, Type 6 | 7 | class SuperSamplingAA(PipelineNode): 8 | 9 | """ 10 | Performs anti-aliasing by accumulating multiple render samples into a single texture. 11 | """ 12 | 13 | def __init__(self, pipeline): 14 | PipelineNode.__init__(self, pipeline) 15 | self.resolution = None 16 | 17 | @classmethod 18 | def reflect_inputs(cls): 19 | inputs = {} 20 | inputs['Color'] = Parameter('', Type.TEXTURE) 21 | return inputs 22 | 23 | @classmethod 24 | def reflect_outputs(cls): 25 | outputs = {} 26 | outputs['Color'] = Parameter('', Type.TEXTURE) 27 | return outputs 28 | 29 | def setup_render_targets(self, resolution): 30 | self.t_color = Texture(resolution, GL_RGBA16F) 31 | self.fbo = RenderTarget([self.t_color]) 32 | 33 | def execute(self, parameters): 34 | inputs = parameters['IN'] 35 | outputs = parameters['OUT'] 36 | 37 | if self.pipeline.resolution != self.resolution: 38 | self.setup_render_targets(self.pipeline.resolution) 39 | self.resolution = self.pipeline.resolution 40 | 41 | if self.pipeline.is_new_frame: 42 | self.fbo.clear([(0,0,0,0)]) 43 | if inputs['Color']: 44 | self.pipeline.blend_texture(inputs['Color'], self.fbo, 1.0 / (self.pipeline.sample_count + 1)) 45 | outputs['Color'] = self.t_color 46 | 47 | NODE = SuperSamplingAA 48 | -------------------------------------------------------------------------------- /Malt/PipelineNode.py: -------------------------------------------------------------------------------- 1 | class PipelineNode(): 2 | 3 | def __init__(self, pipeline): 4 | self.pipeline = pipeline 5 | 6 | @staticmethod 7 | def get_pass_type(): 8 | return None 9 | 10 | @classmethod 11 | def static_reflect(cls, name, inputs, outputs): 12 | meta = {} 13 | if cls.__doc__: 14 | meta['doc'] = cls.__doc__ 15 | dictionary = { 16 | 'name' : name, 17 | 'type' : 'void', 18 | 'file' : 'Render', 19 | 'parameters' : [], 20 | 'pass_type' : cls.get_pass_type(), 21 | 'meta' : meta, 22 | } 23 | for name, input in inputs.items(): 24 | meta = {} 25 | if input.doc: 26 | meta['doc'] = input.doc 27 | if input.subtype: 28 | meta['subtype'] = input.subtype 29 | if input.default_value is not None and input.default_value != input.type_string(): 30 | meta['default'] = input.default_value 31 | dictionary['parameters'].append({ 32 | 'name' : name, 33 | 'type' : input, 34 | 'size' : input.size, 35 | 'io' : 'in', 36 | 'meta' : meta, 37 | }) 38 | for name, output in outputs.items(): 39 | meta = {} 40 | if output.doc: 41 | meta['doc'] = output.doc 42 | if output.subtype: 43 | meta['subtype'] = output.subtype 44 | if output.default_value is not None and output.default_value != output.type_string(): 45 | meta['default'] = output.default_value 46 | dictionary['parameters'].append({ 47 | 'name' : name, 48 | 'type' : output, 49 | 'size' : output.size, 50 | 'io' : 'out', 51 | 'meta' : {}, 52 | }) 53 | return dictionary 54 | 55 | @classmethod 56 | def reflect(cls): 57 | return cls.static_reflect(cls.__name__, cls.reflect_inputs(), cls.reflect_outputs()) 58 | 59 | @classmethod 60 | def reflect_inputs(cls): 61 | return {} 62 | 63 | @classmethod 64 | def reflect_outputs(cls): 65 | return {} 66 | 67 | def execute(self, parameters): 68 | pass 69 | -------------------------------------------------------------------------------- /Malt/PipelinePlugin.py: -------------------------------------------------------------------------------- 1 | from Malt.Utils import isinstance_str 2 | 3 | class PipelinePlugin(): 4 | 5 | @classmethod 6 | def poll_pipeline(self, pipeline): 7 | return True 8 | 9 | @classmethod 10 | def register_pipeline_parameters(self, parameters): 11 | pass 12 | 13 | @classmethod 14 | def register_pipeline_graphs(self): 15 | return [] 16 | 17 | @classmethod 18 | def register_graph_libraries(self, graphs): 19 | pass 20 | 21 | @classmethod 22 | def blendermalt_register(self): 23 | pass 24 | 25 | @classmethod 26 | def blendermalt_unregister(self): 27 | pass 28 | 29 | @classmethod 30 | def blendermalt_register_nodeitems(self, MaltNodeItemClass): 31 | # Should return a dictionary where keys are category names and values are arrays of MaltNodeItems 32 | return {} 33 | 34 | def load_plugins_from_dir(dir): 35 | import sys, os, importlib 36 | if dir not in sys.path: 37 | sys.path.append(dir) 38 | plugins=[] 39 | for e in os.scandir(dir): 40 | if (e.path.startswith('.') or e.path.startswith('_') or 41 | e.is_file() and e.path.endswith('.py') == False): 42 | continue 43 | try: 44 | ''' 45 | spec = importlib.util.spec_from_file_location("_dynamic_plugin_module_", e.path) 46 | module = importlib.util.module_from_spec(spec) 47 | spec.loader.exec_module(module) 48 | ''' 49 | module = importlib.import_module(e.name) 50 | importlib.reload(module) 51 | plugins.append(module.PLUGIN) 52 | except: 53 | import traceback 54 | traceback.print_exc() 55 | print('FILEPATH : ', e.path) 56 | return plugins 57 | -------------------------------------------------------------------------------- /Malt/Pipelines/MiniPipeline/MiniPipeline.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | 3 | from Malt.GL.GL import * 4 | from Malt.GL.Mesh import Mesh 5 | from Malt.GL.RenderTarget import RenderTarget 6 | from Malt.GL.Shader import Shader, UBO 7 | from Malt.GL.Texture import Texture 8 | 9 | from Malt.Pipeline import * 10 | 11 | class MiniPipeline(Pipeline): 12 | 13 | DEFAULT_SHADER = None 14 | 15 | def __init__(self, plugins=[]): 16 | super().__init__(plugins) 17 | 18 | self.parameters.world['Background Color'] = Parameter((0.5,0.5,0.5,1), Type.FLOAT, 4) 19 | 20 | if MiniPipeline.DEFAULT_SHADER is None: 21 | source = ''' 22 | #include "Common.glsl" 23 | 24 | #ifdef VERTEX_SHADER 25 | void main() 26 | { 27 | DEFAULT_VERTEX_SHADER(); 28 | } 29 | #endif 30 | 31 | #ifdef PIXEL_SHADER 32 | layout (location = 0) out vec4 RESULT; 33 | void main() 34 | { 35 | PIXEL_SETUP_INPUT(); 36 | RESULT = vec4(1); 37 | } 38 | #endif 39 | ''' 40 | MiniPipeline.DEFAULT_SHADER = self.compile_material_from_source('mesh', source) 41 | 42 | self.default_shader = MiniPipeline.DEFAULT_SHADER 43 | 44 | def compile_material_from_source(self, material_type, source, include_paths=[]): 45 | return { 46 | 'MAIN_PASS' : self.compile_shader_from_source( 47 | source, include_paths, ['MAIN_PASS'] 48 | ) 49 | } 50 | 51 | def setup_render_targets(self, resolution): 52 | self.t_depth = Texture(resolution, GL_DEPTH_COMPONENT32F) 53 | self.t_main_color = Texture(resolution, GL_RGBA32F) 54 | self.fbo_main = RenderTarget([self.t_main_color], self.t_depth) 55 | 56 | def do_render(self, resolution, scene, is_final_render, is_new_frame): 57 | shader_resources = { 'COMMON_UNIFORMS' : self.common_buffer } 58 | 59 | self.fbo_main.clear([scene.world_parameters['Background Color']], 1) 60 | 61 | self.draw_scene_pass(self.fbo_main, scene.batches, 'MAIN_PASS', self.default_shader['MAIN_PASS'], shader_resources) 62 | 63 | return { 'COLOR' : self.t_main_color } 64 | 65 | 66 | PIPELINE = MiniPipeline 67 | -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/Defaults/defaults.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/Malt/Pipelines/NPR_Pipeline/Defaults/defaults.blend -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/NPR_LightShaders.py: -------------------------------------------------------------------------------- 1 | 2 | from Malt.GL.GL import * 3 | from Malt.GL.Shader import UBO 4 | from Malt.GL.Texture import TextureArray 5 | from Malt.GL.RenderTarget import ArrayLayerTarget, RenderTarget 6 | 7 | from Malt.Render import Lighting 8 | 9 | from Malt.PipelineGraph import * 10 | 11 | import ctypes 12 | 13 | class NPR_LightShaders(): 14 | 15 | def __init__(self): 16 | class C_NPR_LightShadersBuffer(ctypes.Structure): 17 | _fields_ = [ 18 | ('custom_shading_index', ctypes.c_int*Lighting.MAX_LIGHTS), 19 | ] 20 | self.data = C_NPR_LightShadersBuffer() 21 | self.custom_shading_count = 0 22 | self.UBO = UBO() 23 | 24 | self.texture = None 25 | self.fbos = None 26 | 27 | def load(self, pipeline, depth_texture, scene): 28 | self.custom_shading_count = 0 29 | for i, light in enumerate(scene.lights): 30 | custom_shading_index = -1 31 | if light.parameters['Shader'] is not None: 32 | custom_shading_index = self.custom_shading_count 33 | self.custom_shading_count += 1 34 | self.data.custom_shading_index[i] = custom_shading_index 35 | 36 | self.UBO.load_data(self.data) 37 | 38 | if self.custom_shading_count == 0: 39 | return 40 | 41 | lights = [l for l in scene.lights if l.parameters['Shader'] is not None] 42 | tex = self.texture 43 | if tex is None or tex.resolution != depth_texture.resolution or tex.length < len(lights): 44 | self.texture = TextureArray(depth_texture.resolution, len(lights), GL_RGB32F) 45 | self.fbos = [] 46 | for i in range(len(lights)): 47 | self.fbos.append(RenderTarget([ArrayLayerTarget(self.texture, i)])) 48 | 49 | for i, light in enumerate(lights): 50 | material = light.parameters['Shader'] 51 | if material.shader and 'SHADER' in material.shader.keys(): 52 | shader = material.shader['SHADER'] 53 | for resource in scene.shader_resources.values(): 54 | resource.shader_callback(shader) 55 | shader.textures['IN_DEPTH'] = depth_texture 56 | if 'LIGHT_INDEX' in shader.uniforms: 57 | light_index = scene.lights.index(light) 58 | shader.uniforms['LIGHT_INDEX'].set_value(light_index) 59 | pipeline.draw_screen_pass(shader, self.fbos[i]) 60 | 61 | def shader_callback(self, shader): 62 | if 'LIGHTS_CUSTOM_SHADING' in shader.uniform_blocks: 63 | self.UBO.bind(shader.uniform_blocks['LIGHTS_CUSTOM_SHADING']) 64 | shader.textures['IN_LIGHT_CUSTOM_SHADING'] = self.texture 65 | -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/Nodes/Render/ScreenPass.py: -------------------------------------------------------------------------------- 1 | from Malt.GL import GL 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.RenderTarget import RenderTarget 4 | from Malt.PipelineNode import PipelineNode 5 | from Malt.PipelineParameters import Parameter, Type 6 | from Malt.Scene import TextureShaderResource 7 | 8 | 9 | class ScreenPass(PipelineNode): 10 | """ 11 | Renders a full screen shader pass. 12 | The node sockets are dynamic, based on the shader selected. 13 | """ 14 | 15 | def __init__(self, pipeline): 16 | PipelineNode.__init__(self, pipeline) 17 | self.resolution = None 18 | self.texture_targets = {} 19 | self.render_target = None 20 | self.custom_io = [] 21 | 22 | @staticmethod 23 | def get_pass_type(): 24 | return 'Screen.SCREEN_SHADER' 25 | 26 | def execute(self, parameters): 27 | inputs = parameters['IN'] 28 | outputs = parameters['OUT'] 29 | material = parameters['PASS_MATERIAL'] 30 | custom_io = parameters['CUSTOM_IO'] 31 | 32 | if self.pipeline.resolution != self.resolution or self.custom_io != custom_io: 33 | self.texture_targets = {} 34 | for io in custom_io: 35 | if io['io'] == 'out': 36 | if io['type'] == 'Texture':#TODO 37 | self.texture_targets[io['name']] = Texture(self.pipeline.resolution, GL.GL_RGBA16F) 38 | self.render_target = RenderTarget([*self.texture_targets.values()]) 39 | self.resolution = self.pipeline.resolution 40 | self.custom_io = custom_io 41 | 42 | self.render_target.clear([(0,0,0,0)]*len(self.texture_targets)) 43 | 44 | if material and material.shader and 'SHADER' in material.shader: 45 | shader = material.shader['SHADER'] 46 | for io in custom_io: 47 | if io['io'] == 'in': 48 | if io['type'] == 'Texture':#TODO 49 | from Malt.SourceTranspiler import GLSLTranspiler 50 | glsl_name = GLSLTranspiler.custom_io_reference('IN', 'SCREEN_SHADER', io['name']) 51 | shader.textures[glsl_name] = inputs[io['name']] 52 | self.pipeline.common_buffer.shader_callback(shader) 53 | shader.uniforms['RENDER_LAYER_MODE'].set_value(False) 54 | self.pipeline.draw_screen_pass(shader, self.render_target) 55 | 56 | for io in custom_io: 57 | if io['io'] == 'out': 58 | if io['type'] == 'Texture':#TODO 59 | outputs[io['name']] = self.texture_targets[io['name']] 60 | 61 | 62 | NODE = ScreenPass 63 | -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/Nodes/RenderLayer/MainPass.py: -------------------------------------------------------------------------------- 1 | from Malt.GL.GL import * 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.RenderTarget import RenderTarget 4 | from Malt.PipelineNode import PipelineNode 5 | from Malt.PipelineParameters import Parameter, Type 6 | from Malt.Scene import TextureShaderResource 7 | 8 | class MainPass(PipelineNode): 9 | """ 10 | Renders the scene geometry using the *Mesh Main Pass*. 11 | The node sockets are dynamic, based on the *Main Pass Custom IO*. 12 | If *Normal Depth/ID* is empty, the *Pre Pass* *Normal Depth/ID* will be used. 13 | """ 14 | 15 | def __init__(self, pipeline): 16 | PipelineNode.__init__(self, pipeline) 17 | self.resolution = None 18 | self.t_depth = None 19 | 20 | @staticmethod 21 | def get_pass_type(): 22 | return 'Mesh.MAIN_PASS_PIXEL_SHADER' 23 | 24 | @classmethod 25 | def reflect_inputs(cls): 26 | inputs = {} 27 | inputs['Scene'] = Parameter('Scene', Type.OTHER) 28 | inputs['Normal Depth'] = Parameter('', Type.TEXTURE) 29 | inputs['ID'] = Parameter('', Type.TEXTURE) 30 | return inputs 31 | 32 | @classmethod 33 | def reflect_outputs(cls): 34 | outputs = {} 35 | return outputs 36 | 37 | def setup_render_targets(self, resolution, t_depth, custom_io): 38 | self.custom_targets = {} 39 | for io in custom_io: 40 | if io['io'] == 'out' and io['type'] == 'Texture':#TODO 41 | formats = { 42 | 'float' : GL.GL_R16F, 43 | 'vec2' : GL.GL_RG16F, 44 | 'vec3' : GL.GL_RGB16F, 45 | 'vec4' : GL.GL_RGBA16F, 46 | } 47 | self.custom_targets[io['name']] = Texture(resolution, formats[io['subtype']]) 48 | self.t_depth = t_depth 49 | self.fbo = RenderTarget([*self.custom_targets.values()], self.t_depth) 50 | 51 | def execute(self, parameters): 52 | inputs = parameters['IN'] 53 | outputs = parameters['OUT'] 54 | custom_io = parameters['CUSTOM_IO'] 55 | 56 | scene = inputs['Scene'] 57 | if scene is None: 58 | return 59 | t_normal_depth = inputs['Normal Depth'] 60 | t_id = inputs['ID'] 61 | 62 | shader_resources = scene.shader_resources.copy() 63 | if t_normal_depth: 64 | shader_resources['IN_NORMAL_DEPTH'] = TextureShaderResource('IN_NORMAL_DEPTH', t_normal_depth) 65 | if t_id: 66 | shader_resources['IN_ID'] = TextureShaderResource('IN_ID', t_id) 67 | 68 | t_depth = shader_resources['T_DEPTH'].texture 69 | if self.pipeline.resolution != self.resolution or self.custom_io != custom_io or t_depth != self.t_depth: 70 | self.setup_render_targets(self.pipeline.resolution, t_depth, custom_io) 71 | self.resolution = self.pipeline.resolution 72 | self.custom_io = custom_io 73 | 74 | for io in custom_io: 75 | if io['io'] == 'in': 76 | if io['type'] == 'Texture':#TODO 77 | from Malt.SourceTranspiler import GLSLTranspiler 78 | glsl_name = GLSLTranspiler.custom_io_reference('IN', 'MAIN_PASS_PIXEL_SHADER', io['name']) 79 | shader_resources['CUSTOM_IO'+glsl_name] = TextureShaderResource(glsl_name, inputs[io['name']]) 80 | 81 | self.fbo.clear([(0,0,0,0)] * len(self.fbo.targets)) 82 | self.pipeline.draw_scene_pass(self.fbo, scene.batches, 'MAIN_PASS', self.pipeline.default_shader['MAIN_PASS'], 83 | shader_resources, GL_EQUAL) 84 | 85 | outputs.update(self.custom_targets) 86 | 87 | NODE = MainPass 88 | -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/Shaders/NPR_Intellisense.glsl: -------------------------------------------------------------------------------- 1 | //This is just for getting text editor autocompletion on user shaders. 2 | //It doesn't have any effect at runtime 3 | #ifdef __INTELLISENSE__ 4 | 5 | #define VERTEX_SHADER 6 | #define PIXEL_SHADER 7 | 8 | #define IS_MESH_SHADER 9 | 10 | #define CUSTOM_VERTEX_DISPLACEMENT 11 | #define SHADOW_PASS 12 | #define PRE_PASS 13 | #define MAIN_PASS 14 | 15 | #define IS_LIGHT_SHADER 16 | 17 | #define IS_SCREEN_SHADER 18 | 19 | #endif //__INTELLISENSE__ 20 | -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/Shaders/NPR_LightShader.glsl: -------------------------------------------------------------------------------- 1 | #define NO_NORMAL_INPUT 2 | #define NO_UV_INPUT 3 | #define NO_VERTEX_COLOR_INPUT 4 | #define NO_MODEL_INPUT 5 | #define NO_ID_INPUT 6 | 7 | #ifdef PIXEL_SHADER 8 | float DEFERRED_PIXEL_DEPTH; 9 | #define CUSTOM_PIXEL_DEPTH DEFERRED_PIXEL_DEPTH 10 | #endif 11 | 12 | #include "NPR_Intellisense.glsl" 13 | #include "Common.glsl" 14 | #include "Lighting/Lighting.glsl" 15 | 16 | uniform int LIGHT_INDEX; 17 | 18 | #ifdef VERTEX_SHADER 19 | void main() 20 | { 21 | DEFAULT_SCREEN_VERTEX_SHADER(); 22 | } 23 | #endif 24 | 25 | #ifdef PIXEL_SHADER 26 | 27 | uniform sampler2D IN_DEPTH; 28 | 29 | layout (location = 0) out vec3 RESULT; 30 | 31 | void LIGHT_SHADER(vec3 relative_coordinates, vec3 uvw, inout vec3 color, inout float attenuation); 32 | 33 | void main() 34 | { 35 | PIXEL_SETUP_INPUT(); 36 | 37 | float depth = texelFetch(IN_DEPTH, screen_pixel(), 0).x; 38 | DEFERRED_PIXEL_DEPTH = depth; 39 | POSITION = screen_to_camera(screen_uv(), depth); 40 | POSITION = transform_point(inverse(CAMERA), POSITION); 41 | 42 | Light L = LIGHTS.lights[LIGHT_INDEX]; 43 | LitSurface LS = lit_surface(POSITION, vec3(0), L, false); 44 | 45 | vec3 light_space = vec3(0); 46 | vec3 uvw = vec3(0); 47 | 48 | if(L.type == LIGHT_SPOT) 49 | { 50 | light_space = project_point(LIGHTS.spot_matrices[L.type_index], POSITION); 51 | uvw.xy = light_space.xy * 0.5 + 0.5; 52 | } 53 | if(L.type == LIGHT_SUN) 54 | { 55 | vec3 z = L.direction; 56 | vec3 c = vec3(0,0,1); 57 | if(abs(dot(z, c)) < 1.0) 58 | { 59 | vec3 x = normalize(cross(c, z)); 60 | vec3 y = normalize(cross(x, z)); 61 | mat3 rotation = mat3(x,y,z); 62 | mat4 m = mat4_translation(L.position) * mat4(rotation); 63 | m = inverse(m); 64 | 65 | light_space = transform_point(m, POSITION); 66 | } 67 | else 68 | { 69 | light_space = POSITION; 70 | light_space -= L.position; 71 | } 72 | 73 | uvw.xy = light_space.xy; 74 | } 75 | if(L.type == LIGHT_POINT) 76 | { 77 | light_space = POSITION - L.position; 78 | light_space /= L.radius; 79 | 80 | uvw = normalize(light_space); 81 | } 82 | 83 | vec3 color = L.color; 84 | float attenuation = LS.P; 85 | 86 | LIGHT_SHADER(light_space, uvw, color, attenuation); 87 | 88 | RESULT = color * attenuation; 89 | } 90 | 91 | #endif //PIXEL_SHADER 92 | -------------------------------------------------------------------------------- /Malt/Pipelines/NPR_Pipeline/Shaders/NPR_ScreenShader.glsl: -------------------------------------------------------------------------------- 1 | #define NO_UV_INPUT 2 | #define NO_VERTEX_COLOR_INPUT 3 | #define NO_MODEL_INPUT 4 | 5 | #include "NPR_Intellisense.glsl" 6 | 7 | #ifdef PIXEL_SHADER 8 | vec3 DEFERRED_TRUE_NORMAL; 9 | #define CUSTOM_TRUE_NORMAL DEFERRED_TRUE_NORMAL 10 | 11 | float DEFERRED_PIXEL_DEPTH; 12 | #define CUSTOM_PIXEL_DEPTH DEFERRED_PIXEL_DEPTH 13 | #endif 14 | 15 | #include "Common.glsl" 16 | 17 | #ifdef VERTEX_SHADER 18 | void main() 19 | { 20 | DEFAULT_SCREEN_VERTEX_SHADER(); 21 | } 22 | #endif 23 | 24 | #ifdef PIXEL_SHADER 25 | uniform sampler2D IN_NORMAL_DEPTH; 26 | uniform usampler2D IN_ID; 27 | 28 | uniform bool RENDER_LAYER_MODE = false; 29 | uniform bool DEFERRED_MODE = false; 30 | 31 | void SCREEN_SHADER(); 32 | 33 | void main() 34 | { 35 | PIXEL_SETUP_INPUT(); 36 | 37 | DEFERRED_TRUE_NORMAL = reconstruct_normal(IN_NORMAL_DEPTH, 3, screen_pixel()); 38 | DEFERRED_PIXEL_DEPTH = texelFetch(IN_NORMAL_DEPTH, screen_pixel(), 0).w; 39 | 40 | if(RENDER_LAYER_MODE) 41 | { 42 | vec4 normal_depth = texture(IN_NORMAL_DEPTH, UV[0]); 43 | if(DEFERRED_MODE && normal_depth.w == 1.0) 44 | { 45 | discard; 46 | } 47 | POSITION = screen_to_camera(UV[0], normal_depth.w); 48 | POSITION = transform_point(inverse(CAMERA), POSITION); 49 | NORMAL = normal_depth.xyz; 50 | ID = texture(IN_ID, UV[0]); 51 | } 52 | 53 | SCREEN_SHADER(); 54 | } 55 | #endif 56 | 57 | #include "NPR_Pipeline/NPR_Filters.glsl" 58 | #include "NPR_Pipeline/NPR_Shading.glsl" 59 | #include "NPR_Pipeline/NPR_Shading2.glsl" 60 | -------------------------------------------------------------------------------- /Malt/Render/Common.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | from Malt.GL.Shader import UBO 4 | 5 | class C_CommonBuffer(ctypes.Structure): 6 | _fields_ = [ 7 | ('CAMERA', ctypes.c_float*16), 8 | ('PROJECTION', ctypes.c_float*16), 9 | ('RESOLUTION', ctypes.c_int*2), 10 | ('SAMPLE_OFFSET', ctypes.c_float*2), 11 | ('SAMPLE_COUNT', ctypes.c_int), 12 | ('FRAME', ctypes.c_int), 13 | ('TIME', ctypes.c_float), 14 | ('__padding', ctypes.c_int), 15 | ] 16 | 17 | def bake_sample_offset(projection_matrix, sample_offset, resolution): 18 | offset_x = sample_offset[0] / resolution[0] 19 | offset_y = sample_offset[1] / resolution[1] 20 | if projection_matrix[-1] == 1.0: 21 | #Orthographic camera 22 | projection_matrix[12] += offset_x 23 | projection_matrix[13] += offset_y 24 | else: 25 | #Perspective camera 26 | projection_matrix[8] += offset_x 27 | projection_matrix[9] += offset_y 28 | 29 | class CommonBuffer(): 30 | 31 | def __init__(self): 32 | self.data = C_CommonBuffer() 33 | self.UBO = UBO() 34 | 35 | def load(self, scene, resolution, sample_offset=(0,0), sample_count=0, camera=None, projection = None): 36 | self.data.CAMERA = tuple(camera if camera else scene.camera.camera_matrix) 37 | self.data.PROJECTION = tuple(projection if projection else scene.camera.projection_matrix) 38 | self.data.RESOLUTION = resolution 39 | self.data.SAMPLE_OFFSET = sample_offset 40 | self.data.SAMPLE_COUNT = sample_count 41 | self.data.FRAME = scene.frame 42 | self.data.TIME = scene.time 43 | 44 | bake_sample_offset(self.data.PROJECTION, sample_offset, resolution) 45 | 46 | self.UBO.load_data(self.data) 47 | 48 | def bind(self, block): 49 | self.UBO.bind(block) 50 | 51 | def shader_callback(self, shader): 52 | if 'COMMON_UNIFORMS' in shader.uniform_blocks: 53 | self.bind(shader.uniform_blocks['COMMON_UNIFORMS']) 54 | -------------------------------------------------------------------------------- /Malt/Render/DepthToCompositeDepth.py: -------------------------------------------------------------------------------- 1 | from Malt.GL.GL import * 2 | from Malt.GL.Texture import Texture 3 | from Malt.GL.RenderTarget import RenderTarget 4 | 5 | _shader_src=''' 6 | #include "Passes/DepthToBlenderDepth.glsl" 7 | ''' 8 | 9 | _SHADER = None 10 | 11 | class CompositeDepth(): 12 | 13 | def __init__(self): 14 | self.t = None 15 | self.fbo = None 16 | 17 | self.shader = None 18 | 19 | def render(self, pipeline, common_buffer, depth_texture, depth_channel=0): 20 | if self.t is None or self.t.resolution != depth_texture.resolution: 21 | self.t = Texture(depth_texture.resolution, GL_R32F) 22 | self.fbo = RenderTarget([self.t]) 23 | 24 | if self.shader == None: 25 | global _SHADER 26 | if _SHADER is None: _SHADER = pipeline.compile_shader_from_source(_shader_src) 27 | self.shader = _SHADER 28 | 29 | self.shader.textures['DEPTH_TEXTURE'] = depth_texture 30 | self.shader.uniforms['DEPTH_CHANNEL'].set_value(depth_channel) 31 | common_buffer.shader_callback(self.shader) 32 | pipeline.draw_screen_pass(self.shader, self.fbo) 33 | return self.t 34 | -------------------------------------------------------------------------------- /Malt/Render/Sampling.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import random 4 | #Don't share state 5 | random = random.Random() 6 | 7 | #Rotated Grid Super Sampling pattern 8 | #https://en.wikipedia.org/wiki/Supersampling#Rotated_grid 9 | def get_RGSS_samples(grid_size, width=1.0): 10 | samples = [] 11 | for x in range(0, grid_size): 12 | for y in range(0, grid_size): 13 | _x = (x / grid_size) * 2.0 - 1.0 #(-1 ... +1 range) 14 | _y = (y / grid_size) * 2.0 - 1.0 #(-1 ... +1 range) 15 | 16 | angle = math.atan(1/2) 17 | sin = math.sin(angle) 18 | cos = math.cos(angle) 19 | r_x = _x * cos - _y * sin 20 | r_y = _x * sin + _y * cos 21 | 22 | scale = math.sqrt(5)/2 23 | r_x *= scale 24 | 25 | #discard samples where radius > 1 26 | if r_x * r_x + r_y * r_y <= 1: 27 | r_x *= width 28 | r_y *= width 29 | samples.append((r_x,r_y)) 30 | 31 | random.seed(0) 32 | #randomize the sampling order to get better early results 33 | samples = sorted(samples, key=lambda k: random.random()) 34 | 35 | #Make sure there's at least one sample 36 | if len(samples) == 0: 37 | samples = [(0,0)] 38 | 39 | return samples 40 | 41 | 42 | #Random sampling. Best results at high sample counts 43 | def get_random_samples(grid_size, width=1.0): 44 | random.seed(0) 45 | samples = [] 46 | for i in range(0, grid_size * grid_size): 47 | x = 2 48 | y = 2 49 | 50 | #discard samples where radius > 1 51 | while x*x + y*y > 1.0: 52 | x = random.random() * 2.0 - 1.0 #(-1 ... +1 range) 53 | y = random.random() * 2.0 - 1.0 #(-1 ... +1 range) 54 | 55 | x *= width 56 | y *= width 57 | 58 | samples.append((x,y)) 59 | 60 | #Make sure there's at least one sample 61 | if len(samples) == 0: 62 | samples = [(0,0)] 63 | 64 | return samples 65 | -------------------------------------------------------------------------------- /Malt/Render/readme.md: -------------------------------------------------------------------------------- 1 | # Render Library 2 | 3 | Any *Pipeline* agnostic render utility that can't be implemented only as *GLSL* code should go here. 4 | From simple *Uniform Buffer Objects* ([Common.py](Common.py)) and pure *Python* utilities ([Sampling.py](Sampling.py)) to more complex features like Lighting and Shadowmaps ([Lighting.py](Lighting.py)). 5 | 6 | *Render* modules don't follow any specific API and while similar features share a similar interface, they are free to implement the most appropriate for their needs. 7 | 8 | As more features are implemented and common patterns arise, a *Pipeline Plugins* API can be designed so using this utilities requires less manual communication from the *Pipeline* side. 9 | 10 | -------------------------------------------------------------------------------- /Malt/Scene.py: -------------------------------------------------------------------------------- 1 | class Camera(): 2 | 3 | def __init__(self, camera_matrix, projection_matrix, parameters={}): 4 | self.camera_matrix = camera_matrix 5 | self.projection_matrix = projection_matrix 6 | self.parameters = parameters 7 | 8 | class Material(): 9 | 10 | def __init__(self, shader, parameters={}): 11 | self.shader = shader 12 | self.parameters = parameters 13 | 14 | class Mesh(): 15 | 16 | def __init__(self, mesh, parameters={}): 17 | self.mesh = mesh 18 | self.parameters = parameters 19 | 20 | class Object(): 21 | 22 | def __init__(self, matrix, mesh, material, parameters={}, mirror_scale=False, tags=[]): 23 | self.matrix = matrix 24 | self.mesh = mesh 25 | self.material = material 26 | self.parameters = parameters 27 | self.mirror_scale = mirror_scale 28 | self.tags = tags 29 | 30 | class Light(): 31 | 32 | def __init__(self): 33 | self.type = 0 34 | self.color = (0,0,0) 35 | self.parameters = {} 36 | self.position = (0,0,0) 37 | self.direction = (0,0,0) 38 | self.sun_max_distance = 0 39 | self.spot_angle = 0 40 | self.spot_blend = 0 41 | self.radius = 0 42 | self.matrix = None 43 | 44 | class Scene(): 45 | 46 | def __init__(self): 47 | self.camera = None 48 | self.objects = [] 49 | self.lights = [] 50 | self.parameters = {} 51 | self.world_parameters = {} 52 | self.frame = 0 53 | self.time = 0 54 | 55 | self.batches = None 56 | self.shader_resources = {} 57 | 58 | class ShaderResource(): 59 | 60 | def shader_callback(self, shader): 61 | pass 62 | 63 | class TextureShaderResource(): 64 | 65 | def __init__(self, name, texture): 66 | self.name = name 67 | self.texture = texture 68 | 69 | def shader_callback(self, shader): 70 | if self.name in shader.textures.keys(): 71 | shader.textures[self.name] = self.texture 72 | -------------------------------------------------------------------------------- /Malt/Shaders/Common/Hash.glsl: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HASH_GLSL 2 | #define COMMON_HASH_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Math; internal=True; 6 | */ 7 | 8 | uvec4 _pcg4d(uvec4 v) 9 | { 10 | //http://www.jcgt.org/published/0009/03/02/ 11 | v = v * 1664525u + 1013904223u; 12 | v.x += v.y*v.w; 13 | v.y += v.z*v.x; 14 | v.z += v.x*v.y; 15 | v.w += v.y*v.z; 16 | v ^= v >> 16u; 17 | v.x += v.y*v.w; 18 | v.y += v.z*v.x; 19 | v.z += v.x*v.y; 20 | v.w += v.y*v.z; 21 | return v; 22 | } 23 | 24 | vec4 _pcg4d(vec4 v) 25 | { 26 | uvec4 u = _pcg4d(floatBitsToUint(v)); 27 | return vec4(u) / float(0xffffffffU); 28 | } 29 | 30 | vec4 hash(float v){ return _pcg4d(vec4(v,0,0,0)); } 31 | vec4 hash(vec2 v){ return _pcg4d(vec4(v,0,0)); } 32 | vec4 hash(vec3 v){ return _pcg4d(vec4(v,0)); } 33 | /* META @meta: internal=false; @v:subtype=Data; */ 34 | vec4 hash(vec4 v){ return _pcg4d(v); } 35 | 36 | #endif // COMMON_HASH_GLSL 37 | -------------------------------------------------------------------------------- /Malt/Shaders/Common/Mapping.glsl: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_MAPPING_GLSL 2 | #define COMMON_MAPPING_GLSL 3 | 4 | #include "Common.glsl" 5 | 6 | /* META GLOBAL 7 | @meta: category=Vector; internal=True; 8 | */ 9 | 10 | vec3 view_direction(); 11 | vec3 transform_normal(mat4 matrix, vec3 normal); 12 | 13 | /* META 14 | @meta: label=Matcap UV; 15 | @normal: subtype=Normal; default=NORMAL; 16 | */ 17 | vec2 matcap_uv(vec3 normal) 18 | { 19 | vec3 N = transform_normal(CAMERA, normal); 20 | vec3 I = transform_normal(CAMERA, -view_direction()); 21 | 22 | vec3 x = vec3(1,0,0); 23 | vec3 tangent = normalize(x - I * dot(x, I)); 24 | vec3 y = vec3(0,1,0); 25 | vec3 bitangent = normalize(y - I * dot(y, I)); 26 | 27 | vec3 screen_normal = vec3 28 | ( 29 | dot(N, tangent), 30 | dot(N, bitangent), 31 | dot(N, I) 32 | ); 33 | 34 | screen_normal = normalize(screen_normal); 35 | 36 | return screen_normal.xy * 0.499 + 0.5; 37 | } 38 | 39 | /* META 40 | @meta: category=Texturing; 41 | @normal: subtype=Normal; default=NORMAL; 42 | */ 43 | vec4 sample_matcap(sampler2D matcap, vec3 normal) 44 | { 45 | return textureLod(matcap, matcap_uv(normal), 0); 46 | } 47 | 48 | /* META 49 | @meta: label=HDRI UV; 50 | @normal: subtype=Normal; default=NORMAL; 51 | */ 52 | vec2 hdri_uv(vec3 normal) 53 | { 54 | vec2 uv = vec2(atan(normal.y, normal.x), asin(normal.z)); 55 | vec2 inverse_atan = vec2(0.1591, 0.3183); 56 | return uv * inverse_atan + 0.5; 57 | } 58 | 59 | /* META 60 | @meta: category=Texturing; label=Sample HDRI; 61 | @normal: subtype=Normal; default=NORMAL; 62 | */ 63 | vec4 sample_hdri(sampler2D hdri, vec3 normal) 64 | { 65 | return textureLod(hdri, hdri_uv(normal), 0); 66 | } 67 | 68 | vec2 curve_view_mapping(vec2 uv, vec3 normal, vec3 tangent, vec3 incoming) 69 | { 70 | vec3 screen_bitangent = transform_normal(CAMERA, cross(incoming, tangent)); 71 | vec3 screen_normal = transform_normal(CAMERA, normal); 72 | float y_grad = dot(screen_bitangent, screen_normal); 73 | return vec2(uv.x, (y_grad + 1) * 0.5); 74 | } 75 | 76 | vec4 sample_flipbook(sampler2D tex, vec2 uv, ivec2 dimensions, int page, bool top_left) 77 | { 78 | int page_count = dimensions.x * dimensions.y; 79 | page = int(mod(page, page_count)); 80 | vec2 offset = vec2( 81 | mod(page, dimensions.x), 82 | floor(top_left ? dimensions.y - 1 - (page / dimensions.y) : (page / dimensions.y)) 83 | ); 84 | uv = (uv + offset) / vec2(dimensions); 85 | return texture(tex, uv); 86 | } 87 | 88 | float pingpong(float a, float b); 89 | 90 | vec4 flowmap(sampler2D tex, vec2 uv, vec2 flow, float progression, int samples) 91 | { 92 | vec4 result; 93 | float fraction = 1.0 / float(samples); 94 | for(int i = 0; i < samples; i++) 95 | { 96 | float flow_scale = fract(progression - i * fraction); 97 | vec4 color = texture(tex, uv - flow * flow_scale); 98 | 99 | float range = fract(progression - (float(i) / float(samples))) * samples; 100 | float p = pingpong(range, 1.0); 101 | float bounds = (range > 0.0 && range < 2.0)? 1.0 : 0.0; 102 | result += color * p * bounds; 103 | } 104 | return result; 105 | } 106 | 107 | #endif //COMMON_MAPPING_GLSL 108 | -------------------------------------------------------------------------------- /Malt/Shaders/Common/Matrix.glsl: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_MATRIX_GLSL 2 | #define COMMON_MATRIX_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Math; subcategory=Matrix; 6 | */ 7 | 8 | #include "Common/Quaternion.glsl" 9 | 10 | /* META 11 | @meta: label=From Translation; 12 | @t: subtype=Vector; 13 | */ 14 | mat4 mat4_translation(vec3 t) 15 | { 16 | mat4 m = mat4(1.0); 17 | m[3] = vec4(t, 1); 18 | return m; 19 | } 20 | 21 | /* META 22 | @meta: label=From Quaternion; 23 | @q: subtype=Quaternion; default=vec4(0,0,0,1); 24 | */ 25 | mat4 mat4_rotation_from_quaternion(vec4 q) 26 | { 27 | return mat4(mat3( 28 | quaternion_transform(q, vec3(1,0,0)), 29 | quaternion_transform(q, vec3(0,1,0)), 30 | quaternion_transform(q, vec3(0,0,1)) 31 | )); 32 | } 33 | 34 | /* META 35 | @meta: label=From Euler; 36 | @e: subtype=Euler; 37 | */ 38 | mat4 mat4_rotation_from_euler(vec3 e) 39 | { 40 | mat4 x = mat4_rotation_from_quaternion(quaternion_from_axis_angle(vec3(1,0,0), e.x)); 41 | mat4 y = mat4_rotation_from_quaternion(quaternion_from_axis_angle(vec3(0,1,0), e.y)); 42 | mat4 z = mat4_rotation_from_quaternion(quaternion_from_axis_angle(vec3(0,0,1), e.z)); 43 | 44 | return z * y * x; 45 | } 46 | 47 | /* META 48 | @meta: label=From Scale; 49 | @s: subtype=Vector; default=vec3(1); 50 | */ 51 | mat4 mat4_scale(vec3 s) 52 | { 53 | return mat4(mat3(s.x,0,0, 0,s.y,0, 0,0,s.z)); 54 | } 55 | 56 | /* META 57 | @meta: label=Is Orthographic; 58 | @matrix: default=mat4(1); 59 | */ 60 | bool is_ortho(mat4 matrix) 61 | { 62 | return matrix[3][3] == 1.0; 63 | } 64 | 65 | #endif // COMMON_MATRIX_GLSL 66 | -------------------------------------------------------------------------------- /Malt/Shaders/Common/Quaternion.glsl: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_QUATERNION_GLSL 2 | #define COMMON_QUATERNION_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Math; subcategory=Quaternion; 6 | */ 7 | 8 | 9 | /* META 10 | @meta: label=From Axis Angle; 11 | @axis: subtype=Normal; 12 | @angle: subtype=Angle; default=0; 13 | */ 14 | vec4 quaternion_from_axis_angle(vec3 axis, float angle) 15 | { 16 | return vec4(axis * sin(0.5 * angle), cos(0.5 * angle)); 17 | } 18 | 19 | /* META 20 | @meta: label=From Vector Delta; 21 | @from: subtype=Normal; 22 | @to: subtype=Normal; 23 | */ 24 | vec4 quaternion_from_vector_delta(vec3 from, vec3 to) 25 | { 26 | return normalize(vec4(cross(from, to), 1.0 + dot(from, to))); 27 | } 28 | 29 | /* META 30 | @meta: label=Inverted; 31 | @a: label=Q; subtype=Quaternion; default=vec4(0,0,0,1); 32 | */ 33 | vec4 quaternion_inverted(vec4 a) 34 | { 35 | return vec4(-a.xyz, a.w); 36 | } 37 | 38 | /* META 39 | @meta: label=Multiply; 40 | @a: subtype=Quaternion; default=vec4(0,0,0,1); 41 | @b: subtype=Quaternion; default=vec4(0,0,0,1); 42 | */ 43 | vec4 quaternion_multiply(vec4 a, vec4 b) 44 | { 45 | return vec4 46 | ( 47 | a.xyz * b.w + b.xyz * a.w + cross(a.xyz, b.xyz), 48 | a.w * b.w - dot(a.xyz, b.xyz) 49 | ); 50 | } 51 | 52 | /* META 53 | @meta: label=Transform; 54 | @a: label=Q; subtype=Quaternion; default=vec4(0,0,0,1); 55 | @vector: subtype=Vector; default=vec3(0); 56 | */ 57 | vec3 quaternion_transform(vec4 a, vec3 vector) 58 | { 59 | vec3 t = cross(a.xyz, vector) * 2.0; 60 | return vector + t * a.w + cross(a.xyz, t); 61 | } 62 | 63 | /* META 64 | @meta: label=Mix; 65 | @a: subtype=Quaternion; default=vec4(0,0,0,1); 66 | @b: subtype=Quaternion; default=vec4(0,0,0,1); 67 | @factor: subtype=Slider; default=0.5; min=0; max=1; 68 | */ 69 | vec4 quaternion_mix(vec4 a, vec4 b, float factor) 70 | { 71 | return normalize(mix(a, b, factor)); 72 | } 73 | 74 | #endif //COMMON_QUATERION_GLSL 75 | -------------------------------------------------------------------------------- /Malt/Shaders/Filters/AO.glsl: -------------------------------------------------------------------------------- 1 | #ifndef AO_GLSL 2 | #define AO_GLSL 3 | 4 | #include "Common/Math.glsl" 5 | #include "Common/Transform.glsl" 6 | 7 | /* META 8 | @meta: internal=true; 9 | @position: subtype=Vector; default=POSITION; 10 | @normal: subtype=Normal; default=NORMAL; 11 | @samples: default=8; 12 | @radius: default=1.0; 13 | @distribution_exponent: default=5.0; 14 | @bias: default=0.01; 15 | */ 16 | float ao(sampler2D depth_texture, int depth_channel, vec3 position, vec3 normal, int samples, float radius, float distribution_exponent, float bias) 17 | { 18 | //Loosely based on https://learnopengl.com/Advanced-Lighting/SSAO 19 | float occlusion = 0; 20 | 21 | if(samples <= 0 || radius <= 0.0) 22 | { 23 | return 1.0; 24 | } 25 | 26 | for(int i = 0; i < samples; i++) 27 | { 28 | // Generate a random TBN matrix 29 | vec3 random_vec = random_per_pixel(i).xyz; 30 | random_vec.xyz = random_vec.xyz * 2.0 - 1.0; 31 | 32 | vec3 normal = transform_normal(CAMERA, normal); 33 | vec3 tangent = normalize(random_vec - normal * dot(random_vec, normal)); 34 | vec3 bitangent = cross(normal, tangent); 35 | mat3 TBN = mat3(tangent, bitangent, normal); 36 | 37 | vec3 random_offset = random_per_pixel(samples+i).xyz; 38 | // Make samples close to the center more likely, based on distribution exponent 39 | random_offset = normalize(random_offset) * pow(length(random_offset), distribution_exponent); 40 | random_offset *= radius; 41 | 42 | vec3 sample_offset = TBN * random_offset; 43 | vec3 sample_position = transform_point(CAMERA, position) + sample_offset; 44 | 45 | vec3 sample_uv = project_point_to_screen_coordinates(PROJECTION, sample_position); 46 | 47 | float sampled_depth = texture(depth_texture, sample_uv.xy)[depth_channel]; 48 | sampled_depth = screen_to_camera(sample_uv.xy, sampled_depth).z; 49 | 50 | float range_check = smoothstep(0.0, 1.0, radius / abs(sample_position.z - sampled_depth)); 51 | 52 | occlusion += (sampled_depth >= sample_position.z + bias ? 1.0 : 0.0) * range_check; 53 | } 54 | 55 | return 1.0 - (occlusion / samples); 56 | } 57 | 58 | #endif //AO_GLSL 59 | -------------------------------------------------------------------------------- /Malt/Shaders/Filters/Bevel.glsl: -------------------------------------------------------------------------------- 1 | #ifndef FILTERS_BEVEL_GLSL 2 | #define FILTERS_BEVEL_GLSL 3 | 4 | #include "Common/Math.glsl" 5 | #include "Common/Transform.glsl" 6 | 7 | /* META 8 | @meta: internal=true; 9 | @id: default=ID[0]; 10 | @samples: default=8; 11 | @radius: default=1.0; 12 | @distribution_exponent: default=5.0; 13 | @hard_bevel_max_dot: default=0.5; 14 | */ 15 | vec3 bevel 16 | ( 17 | sampler2D normal_texture, sampler2D depth_texture, int depth_channel, 18 | uint id, bool filter_by_id, usampler2D id_texture, int id_channel, 19 | int samples, float radius, float distribution_exponent, 20 | bool hard_bevel, float hard_bevel_max_dot 21 | ) 22 | { 23 | vec2 uv = screen_uv(); 24 | 25 | vec3 pixel_normal = texture(normal_texture, uv).xyz; 26 | float pixel_depth = texture(depth_texture, uv)[depth_channel]; 27 | vec3 pixel_position = screen_to_camera(uv, pixel_depth); 28 | 29 | float closest_distance = radius; 30 | 31 | vec3 normal = pixel_normal; 32 | 33 | float screen_radius = radius / pixel_world_size_at(pixel_depth); 34 | 35 | for(int i = 0; i < samples; i++) 36 | { 37 | vec2 offset = random_per_pixel(i).xy; 38 | if(length(offset) > 1) 39 | { 40 | offset = normalize(offset); 41 | } 42 | offset = offset * 2.0 - 1.0; 43 | if(distribution_exponent > 1) 44 | { 45 | offset = pow(abs(offset), vec2(distribution_exponent)) * sign(offset); 46 | } 47 | offset *= screen_radius; 48 | 49 | vec2 offset_uv = uv + (offset / vec2(RESOLUTION)); 50 | ivec2 offset_texel = ivec2(RESOLUTION * offset_uv); 51 | 52 | if(filter_by_id) 53 | { 54 | uint offset_id = texelFetch(id_texture, offset_texel, 0)[id_channel]; 55 | if (offset_id != id) 56 | { 57 | continue; 58 | } 59 | } 60 | 61 | vec3 offset_normal = texelFetch(normal_texture, offset_texel, 0).xyz; 62 | float offset_depth = texelFetch(depth_texture, offset_texel, 0)[depth_channel]; 63 | 64 | vec3 offset_position = screen_to_camera(offset_uv, offset_depth); 65 | float offset_distance = distance(pixel_position, offset_position); 66 | 67 | if(offset_distance < radius) 68 | { 69 | if(hard_bevel) 70 | { 71 | float offset_dot = dot(pixel_normal, offset_normal); 72 | if(offset_dot <= hard_bevel_max_dot) 73 | { 74 | if(offset_distance < closest_distance) 75 | { 76 | closest_distance = offset_distance; 77 | normal = offset_normal; 78 | } 79 | } 80 | } 81 | else 82 | { 83 | normal += offset_normal; 84 | } 85 | } 86 | } 87 | 88 | if(hard_bevel) 89 | { 90 | float mix_factor = 1.0 - (closest_distance / screen_radius); 91 | mix_factor = saturate(pow(mix_factor, distribution_exponent) * distribution_exponent); 92 | return normalize(mix(NORMAL, normalize(NORMAL + normalize(normal)), mix_factor)); 93 | } 94 | else 95 | { 96 | return normalize(normal); 97 | } 98 | } 99 | 100 | #endif //FILTERS_BEVEL_GLSL 101 | -------------------------------------------------------------------------------- /Malt/Shaders/Filters/Curvature.glsl: -------------------------------------------------------------------------------- 1 | #ifndef CURVATURE_GLSL 2 | #define CURVATURE_GLSL 3 | 4 | #include "Common/Math.glsl" 5 | 6 | /* META GLOBAL 7 | @meta: category=Filter; 8 | */ 9 | 10 | /* META 11 | @uv: label=UV; default=UV[0]; 12 | @width: default=1.0; 13 | @x: subtype=Normal; default=vec3(1,0,0); 14 | @y: subtype=Normal; default=vec3(0,1,0); 15 | */ 16 | float curvature(sampler2D normal_texture, vec2 uv, float width, vec3 x, vec3 y) 17 | { 18 | // x and y must be the screen x and y axis in the same coordinate space as the texture normals 19 | vec2 offset = vec2(width) / vec2(textureSize(normal_texture, 0)); 20 | 21 | vec3 l = texture(normal_texture, uv + vec2(-offset.x,0)).xyz; 22 | vec3 r = texture(normal_texture, uv + vec2( offset.x,0)).xyz; 23 | vec3 d = texture(normal_texture, uv + vec2(0,-offset.y)).xyz; 24 | vec3 u = texture(normal_texture, uv + vec2(0, offset.y)).xyz; 25 | 26 | if(width != 1.0) 27 | { 28 | l = normalize(l); 29 | r = normalize(r); 30 | d = normalize(d); 31 | u = normalize(u); 32 | } 33 | 34 | float curvature = (dot(u,y) - dot(d,y)) + (dot(r,x) - dot(l,x)); 35 | 36 | return map_range_clamped(curvature, -1, 1, 0, 1); 37 | } 38 | 39 | #include "Filters/Line.glsl" 40 | 41 | // Like curvature, but discard depth discontinuities 42 | /* META 43 | @meta: internal=true; 44 | @uv: default=UV[0]; 45 | @width: default=1.0; 46 | @x: subtype=Normal; default=vec3(1,0,0); 47 | @y: subtype=Normal; default=vec3(0,1,0); 48 | @depth_range: default=0.1; 49 | */ 50 | float surface_curvature(sampler2D normal_texture, sampler2D depth_texture, int depth_channel, 51 | vec2 uv, float width, vec3 x, vec3 y, float depth_range) 52 | { 53 | float curvature = curvature(normal_texture, uv, width, x, y); 54 | 55 | float delta_depth = _line_detection_depth(depth_texture, depth_channel, uv, width, LINE_DEPTH_MODE_ANY); 56 | 57 | delta_depth /= depth_range; 58 | delta_depth = clamp(delta_depth, 0, 1); 59 | 60 | return mix(curvature, 0.5, delta_depth); 61 | } 62 | 63 | #endif //CURVATURE_GLSL 64 | -------------------------------------------------------------------------------- /Malt/Shaders/Filters/JumpFlood.glsl: -------------------------------------------------------------------------------- 1 | #ifndef JUMP_FLOOD_GLSL 2 | #define JUMP_FLOOD_GLSL 3 | 4 | /* META 5 | @width: default=1.0; 6 | */ 7 | //input should be a texture where x and y are screen space uvs. 8 | //samples with values == vec(-1,-1) are ignored 9 | //More info : https://medium.com/@bgolus/the-quest-for-very-wide-outlines-ba82ed442cd9#c5bb 10 | vec4 jump_flood(sampler2D input_texture, vec2 uv, float width, bool third_channel_scale) 11 | { 12 | vec4 nearest = vec4(-1,-1,-1,-1); 13 | vec2 resolution = vec2(textureSize(input_texture, 0)); 14 | vec2 offset = vec2(width) / resolution; 15 | 16 | for(int x = -1; x <= 1; x++) 17 | { 18 | for(int y = -1; y <= 1; y++) 19 | { 20 | vec2 sample_uv = uv + vec2(x,y) * offset; 21 | vec4 sampled = texture(input_texture, sample_uv); 22 | if(sampled.xy != vec2(-1,-1)) //Check if it's valid 23 | { 24 | float stored_distance = distance(uv * resolution, nearest.xy * resolution); 25 | float sampled_distance = distance(uv * resolution, sampled.xy * resolution); 26 | 27 | if(third_channel_scale) 28 | { 29 | if(nearest.z > 0) 30 | { 31 | stored_distance /= nearest.z; 32 | } 33 | if(sampled.z > 0) 34 | { 35 | sampled_distance /= sampled.z; 36 | } 37 | } 38 | 39 | if(nearest.xy == vec2(-1,-1) || sampled_distance < stored_distance) 40 | { 41 | nearest = sampled; 42 | } 43 | } 44 | } 45 | } 46 | 47 | return nearest; 48 | } 49 | 50 | 51 | #endif //JUMP_FLOOD_GLSL 52 | -------------------------------------------------------------------------------- /Malt/Shaders/Filters/Sharpen.glsl: -------------------------------------------------------------------------------- 1 | #ifndef SHARPEN_GLSL 2 | #define SHARPEN_GLSL 3 | 4 | #include "Filters/Blur.glsl" 5 | 6 | /* META GLOBAL 7 | @meta: category=Filter; subcategory=Sharpen; 8 | */ 9 | 10 | vec4 _sharpen_common(sampler2D tex, vec2 uv, vec4 blurred, float sharpness) 11 | { 12 | vec4 base = texture(tex, uv); 13 | float scaler = 10.0; // Use scalar to put the range of the sharpness value mostly between 0-1 for convenience 14 | return base + (base - blurred) * sharpness * scaler; 15 | } 16 | 17 | /* META 18 | @meta: label=Box; 19 | @tex: label=Texture; 20 | @uv: label=UV; default = UV[0]; 21 | @radius: default=1.0; min=0.0; 22 | @sharpness: default = 0.3; min=0.0; 23 | */ 24 | vec4 box_sharpen(sampler2D tex, vec2 uv, float radius, bool circular, float sharpness) 25 | { 26 | vec4 blurred = box_blur(tex, uv, radius, circular); 27 | return _sharpen_common(tex, uv, blurred, sharpness); 28 | } 29 | 30 | /* META 31 | @meta: label=Gaussian; 32 | @tex: label=Texture; 33 | @uv: label=UV; default = UV[0]; 34 | @radius: default=1.0; min=0.0; 35 | @sigma: default=1.0; 36 | @sharpness: default = 0.3; min=0.0; 37 | */ 38 | vec4 gaussian_sharpen(sampler2D tex, vec2 uv, float radius, float sigma, float sharpness) 39 | { 40 | vec4 blurred = gaussian_blur(tex, uv, radius, sigma); 41 | return _sharpen_common(tex, uv, blurred, sharpness); 42 | } 43 | 44 | /* META 45 | @meta: label=Jitter; 46 | @tex: label=Texture; 47 | @uv: label=UV; default = UV[0]; 48 | @radius: default=1.0; min=0.0; 49 | @distribution_exponent: default=5.0; 50 | @samples: default=8; min=1; 51 | @sharpness: default = 0.3; min=0.0; 52 | */ 53 | vec4 jitter_sharpen(sampler2D tex, vec2 uv, float radius, float distribution_exponent, int samples, float sharpness) 54 | { 55 | vec4 blurred = jitter_blur(tex, uv, radius, distribution_exponent, samples); 56 | return _sharpen_common(tex, uv, blurred, sharpness); 57 | } 58 | 59 | 60 | #endif //SHARPEN_GLSL 61 | -------------------------------------------------------------------------------- /Malt/Shaders/Filters/StructureTensor.glsl: -------------------------------------------------------------------------------- 1 | #ifndef STRUCTURE_TENSOR_GLSL 2 | #define STRUCTURE_TENSOR_GLSL 3 | 4 | // Described in: https://en.wikipedia.org/wiki/Structure_tensor 5 | 6 | /* META 7 | @meta: category=Filter; internal=true; 8 | @uv: default=UV[0]; 9 | */ 10 | vec3 structure_tensor(sampler2D tex, vec2 uv) 11 | { 12 | vec2 texel = 1.0 / textureSize(tex, 0); 13 | 14 | // Stores the texel offset in x,y and the weight in z. the cells with weight 0 are ignored. 15 | // Notice that for v, the axes of the kernel are flipped to effectively rotate the kernel. 16 | // -1 0 1 17 | // -2 0 2 18 | // -1 0 1 19 | vec3 sobel_kernel[6] = vec3[]( 20 | vec3(-1, -1, -1), vec3(+1, -1, +1), 21 | vec3(-1, +0, -2), vec3(+1, +0, +2), 22 | vec3(-1, +1, -1), vec3(+1, -1, +1) 23 | ); 24 | 25 | vec3 u = vec3(0.0); 26 | vec3 v = vec3(0.0); 27 | for(int i = 0; i < 6; i++) 28 | { 29 | vec3 k = sobel_kernel[i]; 30 | u += texture(tex, uv + texel * k.xy).xyz * k.z; 31 | v += texture(tex, uv + texel * k.yx).xyz * k.z; 32 | } 33 | u /= 4.0; 34 | v /= 4.0; 35 | 36 | return vec3( 37 | dot(u, u), 38 | dot(v, v), 39 | dot(u, v) 40 | ); 41 | } 42 | 43 | /* META @meta: internal=true; */ 44 | vec3 flow_from_structure(vec3 s) 45 | { 46 | float l = 0.5 * (s.y + s.x +sqrt(s.y*s.y - 2.0*s.x*s.y + s.x*s.x + 4.0*s.z*s.z)); 47 | vec2 d = vec2(s.x - l, s.z); 48 | return (length(d) > 0.0)? vec3(normalize(d), sqrt(l)) : vec3(0,1,0); 49 | } 50 | 51 | #endif //STRUCTURE_TENSOR_GLSL 52 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Bool.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_BOOL_GLSL 2 | #define NODE_UTILS_2_BOOL_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Math; subcategory=Boolean Logic; 6 | */ 7 | 8 | /*META @meta: label=And;*/ 9 | bool Bool_and(bool a, bool b) { return a && b; } 10 | /*META @meta: label=Or;*/ 11 | bool Bool_or(bool a, bool b) { return a || b; } 12 | /*META @meta: label=Not;*/ 13 | bool Bool_not(bool b) { return !b; } 14 | /*META @meta: label=Equal;*/ 15 | bool Bool_equal(bool a, bool b){ return a == b; } 16 | /*META @meta: label=Not Equal;*/ 17 | bool Bool_not_equal(bool a, bool b){ return a != b; } 18 | /*META @meta: label=If Else; @a: label=If True; @b: label=If False; */ 19 | bool Bool_if_else(bool condition, bool a, bool b){ return condition ? a : b; } 20 | 21 | #endif //NODE_UTILS_2_BOOL_GLSL 22 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Color.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_COLOR_GLSL 2 | #define NODE_UTILS_2_COLOR_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Color; 6 | */ 7 | 8 | /* META 9 | @meta: label=Gradient; subcategory=Color Gradient; 10 | @Coord: label=U; subtype=Slider; min=0.0; max=1.0; 11 | */ 12 | vec4 Color_Gradient_1d(sampler1D Color_Ramp, float Coord) { return texture(Color_Ramp, Coord); } 13 | 14 | /* META 15 | @meta: label=RGB Gradient; subcategory=Color Gradient; 16 | @Coord: label=UVW; subtype=Slider; min=0.0; max=1.0; 17 | */ 18 | vec3 Color_Gradient_3d(sampler1D Color_Ramp, vec3 Coord) { return rgb_gradient(Color_Ramp, Coord); } 19 | 20 | /* META 21 | @meta: label=RGBA Gradient; subcategory=Color Gradient; 22 | @Coord: label=UVWX; subtype=Slider; min=0.0; max=1.0; 23 | */ 24 | vec4 Color_Gradient_4d(sampler1D Color_Ramp, vec4 Coord) { return rgba_gradient(Color_Ramp, Coord); } 25 | 26 | #include "Node Utils 2/ColorBlend.glsl" 27 | 28 | #endif // NODE_UTILS_2_COLOR_GLSL 29 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Filter.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_FILTER_GLSL 2 | #define NODE_UTILS_2_FILTER_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Filter; 6 | */ 7 | 8 | #endif //NODE_UTILS_2_FILTER_GLSL 9 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Int.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_INT_GLSL 2 | #define NODE_UTILS_2_INT_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Math; subcategory=Integer; 6 | */ 7 | 8 | /*META @meta: label=Add;*/ 9 | int Int_add(int a, int b){ return a+b; } 10 | /*META @meta: label=Subtract;*/ 11 | int Int_subtract(int a, int b){ return a-b; } 12 | /*META @meta: label=Multiply;*/ 13 | int Int_multiply(int a, int b){ return a*b; } 14 | /*META @meta: label=Divide;*/ 15 | int Int_divide(int a, int b){ return a/b; } 16 | /*META @meta: label=Modulo;*/ 17 | int Int_modulo(int a, int b){ return a%b; } 18 | 19 | /*META @meta: label=Clamp; @b: label=Min; @c: label=Max; */ 20 | int Int_clamp(int a, int b, int c){ return clamp(a, b, c); } 21 | 22 | /*META @meta: label=Sign;*/ 23 | int Int_sign(int a){ return sign(a); } 24 | /*META @meta: label=Absolute;*/ 25 | int Int_abs(int a){ return abs(a); } 26 | /*META @meta: label=Minimum;*/ 27 | int Int_min(int a, int b){ return min(a,b); } 28 | /*META @meta: label=Maximum;*/ 29 | int Int_max(int a, int b){ return max(a,b); } 30 | 31 | /*META @meta: label=Equal;*/ 32 | bool Int_equal(int a, int b){ return a == b; } 33 | /*META @meta: label=Not Equal;*/ 34 | bool Int_not_equal(int a, int b){ return a != b; } 35 | /*META @meta: label=Greater;*/ 36 | bool Int_greater(int a, int b){ return a > b; } 37 | /*META @meta: label=Greater or Equal;*/ 38 | bool Int_greater_or_equal(int a, int b){ return a >= b; } 39 | /*META @meta: label=Less;*/ 40 | bool Int_less(int a, int b){ return a < b; } 41 | /*META @meta: label=Less or Equal;*/ 42 | bool Int_less_or_equal(int a, int b){ return a <= b; } 43 | 44 | /*META @meta: label=If Else; @a: label=If True; @b: label=If False; */ 45 | int Int_if_else(bool condition, int a, int b){ return condition ? a : b; } 46 | 47 | #endif //NODE_UTILS_2_INT_GLSL 48 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Mat4.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_MAT4_GLSL 2 | #define NODE_UTILS_2_MAT4_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Math; subcategory=Matrix; 6 | */ 7 | 8 | /* META 9 | @meta: label=Inverse; 10 | @a: label=Matrix; default=mat4(1); 11 | */ 12 | mat4 Mat4_inverse(mat4 a) 13 | { 14 | return inverse(a); 15 | } 16 | 17 | /* META 18 | @meta: label=Multiply; 19 | @a: default=mat4(1); 20 | @b: default=mat4(1); 21 | */ 22 | mat4 Mat4_multiply(mat4 a, mat4 b) 23 | { 24 | return a * b; 25 | } 26 | 27 | #endif //NODE_UTILS_2_MAT4_GLSL 28 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Parameters.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_PARAMETERS_GLSL 2 | #define NODE_UTILS_2_PARAMETERS_GLSL 3 | 4 | /*META GLOBAL 5 | @meta: category=Parameters; 6 | */ 7 | 8 | /* META 9 | @meta: label=Boolean; 10 | */ 11 | bool Bool_property(bool b) { return b; } 12 | /* META 13 | @meta: label=Float; 14 | */ 15 | float Float_property(float f) { return f; } 16 | /* META 17 | @meta: label=Integer; 18 | */ 19 | int Int_property(int i) { return i; } 20 | /* META 21 | @meta: label=Vector 2D; 22 | */ 23 | vec2 Vec2_property(vec2 v) { return v; } 24 | 25 | /* META 26 | @meta: label=Vector 3D; 27 | @v: subtype=Vector; 28 | */ 29 | vec3 Vec3_property(vec3 v) { return v; } 30 | /* META 31 | @meta: label=Vector 4D; 32 | @v: subtype=Vector; 33 | */ 34 | vec4 Vec4_property(vec4 v) { return v; } 35 | 36 | /* META 37 | @meta: label=RGB Color; 38 | @v: subtype=Color; 39 | */ 40 | vec3 Vec3_color_property(vec3 v) { return v; } 41 | /* META 42 | @meta: label=RGBA Color; 43 | @v: subtype=Color; 44 | */ 45 | vec4 Vec4_color_property(vec4 v) { return v; } 46 | 47 | #ifdef GL_ARB_bindless_texture 48 | /* META 49 | @meta: label=Color Ramp; 50 | */ 51 | sampler1D Sampler1D_property(sampler1D color_ramp) { return color_ramp; } 52 | /* META 53 | @meta: label=Image; 54 | */ 55 | sampler2D Sampler2D_property(sampler2D image) { return image; } 56 | #endif 57 | 58 | #endif //NODE_UTILS_2_PARAMETERS_GLSL 59 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/Vector.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_VECTOR_GLSL 2 | #define NODE_UTILS_2_VECTOR_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: category=Vector; 6 | */ 7 | 8 | /* META 9 | @Type: subtype=ENUM(Point,Vector,Normal); 10 | @From: subtype=ENUM(Object,World,Camera); 11 | @To: subtype=ENUM(Object,World,Camera,Screen); 12 | @Vector: subtype=Vector; 13 | */ 14 | void Transform( 15 | int Type, 16 | int From, 17 | int To, 18 | inout vec3 Vector 19 | ) 20 | { 21 | mat4 TRANSFORM_CONVERSION_TABLE[3*3] = mat4[3*3]( 22 | mat4(1), MODEL, CAMERA*MODEL, 23 | inverse(MODEL), mat4(1), CAMERA, 24 | inverse(CAMERA*MODEL), inverse(CAMERA), mat4(1) 25 | ); 26 | 27 | mat4 m = TRANSFORM_CONVERSION_TABLE[clamp(From,0,2)*3 + clamp(To,0,2)]; 28 | bool project = To == 3; 29 | if(Type==0)//Point 30 | { 31 | if(project) 32 | { 33 | m = PROJECTION * m; 34 | Vector = project_point_to_screen_coordinates(m, Vector); 35 | } 36 | else 37 | { 38 | Vector = transform_point(m, Vector); 39 | } 40 | } 41 | else 42 | { 43 | if(Type==1)//Vector 44 | { 45 | Vector = transform_direction(m, Vector); 46 | } 47 | if(Type==2)//Normal 48 | { 49 | Vector = transform_normal(m, Vector); 50 | } 51 | if (project) 52 | { 53 | Vector = camera_direction_to_screen_space(Vector); 54 | } 55 | } 56 | } 57 | 58 | /* META 59 | @meta: subcategory=Mapping; label=Point; 60 | @vector: subtype=Vector; default='vec3(0.0)'; 61 | @location: subtype=Vector; 62 | @rotation: subtype=Euler; 63 | @scale: subtype=Vector; default=vec3(1.0); 64 | */ 65 | vec3 mapping_point(vec3 vector, vec3 location, vec3 rotation, vec3 scale) 66 | { 67 | vec3 result = vector * scale; 68 | result = transform_point(mat4_rotation_from_euler(rotation), result); 69 | return result + location; 70 | } 71 | 72 | /* META 73 | @meta: subcategory=Mapping; label=Texture; 74 | @vector: subtype=Vector; default='vec3(0.0)'; 75 | @location: subtype=Vector; 76 | @rotation: subtype=Euler; 77 | @scale: subtype=Vector; default=vec3(1.0); 78 | */ 79 | vec3 mapping_texture(vec3 vector, vec3 location, vec3 rotation, vec3 scale) 80 | { 81 | vec3 result = vector - location; 82 | result = transform_point(inverse(mat4_rotation_from_euler(rotation)), result); 83 | return result / scale; 84 | } 85 | 86 | /* META 87 | @meta: subcategory=Mapping; label=Vector; 88 | @vector: subtype=Vector; default='vec3(0.0)'; 89 | @rotation: subtype=Euler; 90 | @scale: subtype=Vector; default=vec3(1.0); 91 | */ 92 | vec3 mapping_vector(vec3 vector, vec3 rotation, vec3 scale) 93 | { 94 | return transform_direction(mat4_rotation_from_euler(rotation), vector * scale); 95 | } 96 | 97 | /* META 98 | @meta: subcategory=Mapping; label=Normal; 99 | @vector: subtype=Vector; default='vec3(0.0)'; 100 | @rotation: subtype=Euler; 101 | @scale: subtype=Vector; default=vec3(1.0); 102 | */ 103 | 104 | vec3 mapping_normal(vec3 vector, vec3 rotation, vec3 scale) 105 | { 106 | return normalize(mapping_vector(vector, rotation, scale)); 107 | } 108 | 109 | #endif // COMMON_VECTOR_GLSL 110 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils 2/node_utils_2.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_2_GLSL 2 | #define NODE_UTILS_2_GLSL 3 | 4 | #include "Node Utils 2/conversion.glsl" 5 | #include "Node Utils 2/Bool.glsl" 6 | #include "Node Utils 2/Float.glsl" 7 | #include "Node Utils 2/Int.glsl" 8 | #include "Node Utils 2/Mat4.glsl" 9 | #include "Node Utils 2/Vec2.glsl" 10 | #include "Node Utils 2/Vec3.glsl" 11 | #include "Node Utils 2/Vec4.glsl" 12 | 13 | #include "Node Utils 2/Input.glsl" 14 | #include "Node Utils 2/Color.glsl" 15 | #include "Node Utils 2/Vector.glsl" 16 | #include "Node Utils 2/Parameters.glsl" 17 | #include "Node Utils 2/Texturing.glsl" 18 | #include "Node Utils 2/Filter.glsl" 19 | 20 | // Basic common API 21 | #include "Common.glsl" 22 | #include "Filters/AO.glsl" 23 | #include "Filters/Bevel.glsl" 24 | #include "Filters/Blur.glsl" 25 | #include "Filters/Sharpen.glsl" 26 | #include "Filters/Curvature.glsl" 27 | #include "Filters/Kuwahara.glsl" 28 | #include "Filters/Line.glsl" 29 | #include "Filters/StructureTensor.glsl" 30 | #include "Procedural/Noise.glsl" 31 | #include "Procedural/Cell_Noise.glsl" 32 | #include "Procedural/Fractal_Noise.glsl" 33 | #include "Shading/ShadingModels.glsl" 34 | 35 | #endif //NODE_UTILS_GLSL 36 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/bool.glsl: -------------------------------------------------------------------------------- 1 | #ifndef BOOL_GLSL 2 | #define BOOL_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | bool bool_and(bool a, bool b) { return a && b; } 9 | bool bool_or(bool a, bool b) { return a || b; } 10 | bool bool_not(bool b) { return !b; } 11 | 12 | bool bool_equal(bool a, bool b){ return a == b; } 13 | bool bool_not_equal(bool a, bool b){ return a != b; } 14 | 15 | bool if_else(bool condition, bool if_true, bool if_false){ return condition ? if_true : if_false; } 16 | 17 | #endif //BOOL_GLSL 18 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/common.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_COMMON_GLSL 2 | #define NODE_UTILS_COMMON_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | vec3 surface_position() { return POSITION; } 9 | vec3 surface_normal() { return NORMAL; } 10 | vec3 surface_tangent(int index) { return get_tangent(index); } 11 | vec3 surface_bitangent(int index) { return get_bitangent(index); } 12 | vec2 surface_uv(int index) { return UV[index]; } 13 | vec4 surface_vertex_color(int index) { return COLOR[index]; } 14 | 15 | vec3 surface_original_position() { return IO_POSITION; } 16 | vec3 surface_original_normal() { return IO_NORMAL; } 17 | 18 | uvec4 object_id() { return ID; } 19 | 20 | uvec4 object_original_id() { return IO_ID; } 21 | 22 | mat4 model_matrix() { return MODEL; } 23 | mat4 camera_matrix() { return CAMERA; } 24 | mat4 projection_matrix() { return PROJECTION; } 25 | 26 | vec2 render_resolution() { return vec2(RESOLUTION); } 27 | vec2 sample_offset() { return SAMPLE_OFFSET; } 28 | int sample_count() { return SAMPLE_COUNT; } 29 | 30 | int current_frame() { return FRAME; } 31 | float current_time() { return TIME; } 32 | 33 | #endif // NODE_UTILS_COMMON_GLSL 34 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/float.glsl: -------------------------------------------------------------------------------- 1 | #ifndef FLOAT_GLSL 2 | #define FLOAT_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | float float_add(float a, float b){ return a+b; } 9 | float float_subtract(float a, float b){ return a-b; } 10 | float float_multiply(float a, float b){ return a*b; } 11 | float float_divide(float a, float b){ return a/b; } 12 | float float_modulo(float a, float b){ return mod(a,b); } 13 | float float_pow(float f, float e){ return pow(f, e); } 14 | float float_sqrt(float f){ return sqrt(f); } 15 | 16 | float float_round(float f){ return round(f); } 17 | float float_fract(float f){ return fract(f); } 18 | float float_floor(float f){ return floor(f); } 19 | float float_ceil(float f){ return ceil(f); } 20 | 21 | float float_clamp(float f, float min, float max){ return clamp(f, min, max); } 22 | 23 | float float_sign(float f){ return sign(f); } 24 | float float_abs(float f){ return abs(f); } 25 | float float_min(float a, float b){ return min(a,b); } 26 | float float_max(float a, float b){ return max(a,b); } 27 | 28 | float float_mix(float a, float b, float factor){ return mix(a,b,factor); } 29 | 30 | float float_sin(float f) { return sin(f); } 31 | float float_cos(float f) { return cos(f); } 32 | float float_tan(float f) { return tan(f); } 33 | float float_asin(float f) { return asin(f); } 34 | float float_acos(float f) { return acos(f); } 35 | float float_atan(float f) { return atan(f); } 36 | 37 | float float_degrees(float r) { return degrees(r); } 38 | float float_radians(float d) { return radians(d); } 39 | 40 | bool float_equal(float a, float b){ return a == b; } 41 | bool float_not_equal(float a, float b){ return a != b; } 42 | bool float_greater(float a, float b){ return a > b; } 43 | bool float_greater_or_equal(float a, float b){ return a >= b; } 44 | bool float_less(float a, float b){ return a < b; } 45 | bool float_less_or_equal(float a, float b){ return a <= b; } 46 | 47 | float float_if_else(bool condition, float if_true, float if_false){ return condition ? if_true : if_false; } 48 | 49 | #endif //FLOAT_GLSL 50 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/int.glsl: -------------------------------------------------------------------------------- 1 | #ifndef INT_GLSL 2 | #define INT_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | int int_add(int a, int b){ return a+b; } 9 | int int_subtract(int a, int b){ return a-b; } 10 | int int_multiply(int a, int b){ return a*b; } 11 | int int_divide(int a, int b){ return a/b; } 12 | int int_modulo(int a, int b){ return a%b; } 13 | 14 | int int_clamp(int i, int min, int max){ return clamp(i, min, max); } 15 | 16 | int int_sign(int i){ return sign(i); } 17 | int int_abs(int i){ return abs(i); } 18 | int int_min(int a, int b){ return min(a,b); } 19 | int int_max(int a, int b){ return max(a,b); } 20 | 21 | bool int_equal(int a, int b){ return a == b; } 22 | bool int_not_equal(int a, int b){ return a != b; } 23 | bool int_greater(int a, int b){ return a > b; } 24 | bool int_greater_or_equal(int a, int b){ return a >= b; } 25 | bool int_less(int a, int b){ return a < b; } 26 | bool int_less_or_equal(int a, int b){ return a <= b; } 27 | 28 | int int_if_else(bool condition, int if_true, int if_false){ return condition ? if_true : if_false; } 29 | 30 | #endif //INT_GLSL 31 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/node_utils.glsl: -------------------------------------------------------------------------------- 1 | #ifndef NODE_UTILS_GLSL 2 | #define NODE_UTILS_GLSL 3 | 4 | #include "Node Utils/common.glsl" 5 | #include "Node Utils/bool.glsl" 6 | #include "Node Utils/float.glsl" 7 | #include "Node Utils/int.glsl" 8 | #include "Node Utils/packing.glsl" 9 | #include "Node Utils/properties.glsl" 10 | #include "Node Utils/sampler.glsl" 11 | #include "Node Utils/vec2.glsl" 12 | #include "Node Utils/vec3.glsl" 13 | #include "Node Utils/vec4.glsl" 14 | 15 | // Basic common API 16 | #include "Common.glsl" 17 | #include "Filters/AO.glsl" 18 | #include "Filters/Bevel.glsl" 19 | #include "Filters/Blur.glsl" 20 | #include "Filters/Curvature.glsl" 21 | #include "Filters/Line.glsl" 22 | #include "Procedural/Noise.glsl" 23 | #include "Procedural/Cell_Noise.glsl" 24 | #include "Procedural/Fractal_Noise.glsl" 25 | #include "Shading/ShadingModels.glsl" 26 | 27 | #endif //NODE_UTILS_GLSL 28 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/packing.glsl: -------------------------------------------------------------------------------- 1 | #ifndef PACKING_GLSL 2 | #define PACKING_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | uvec4 pack_8bit(vec4 a, vec4 b, vec4 c, vec4 d) 9 | { 10 | return uvec4(packUnorm4x8(a), packUnorm4x8(b), packUnorm4x8(c), packUnorm4x8(d)); 11 | } 12 | 13 | void unpack_8bit(uvec4 packed_vector, out vec4 a, out vec4 b, out vec4 c, out vec4 d) 14 | { 15 | a = unpackUnorm4x8(packed_vector.x); 16 | b = unpackUnorm4x8(packed_vector.y); 17 | c = unpackUnorm4x8(packed_vector.z); 18 | d = unpackUnorm4x8(packed_vector.w); 19 | } 20 | 21 | #endif //PACKING_GLSL 22 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/properties.glsl: -------------------------------------------------------------------------------- 1 | #ifndef PROPERTIES_GLSL 2 | #define PROPERTIES_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | bool bool_property(bool b) { return b; } 9 | 10 | float float_property(float f) { return f; } 11 | 12 | int int_property(int i) { return i; } 13 | 14 | vec2 vec2_property(vec2 v) { return v; } 15 | 16 | /* META @v: subtype=Vector;*/ 17 | vec3 vec3_property(vec3 v) { return v; } 18 | /* META @v: subtype=Vector;*/ 19 | vec4 vec4_property(vec4 v) { return v; } 20 | 21 | vec3 vec3_color_property(vec3 v) { return v; } 22 | vec4 vec4_color_property(vec4 v) { return v; } 23 | 24 | #endif //PROPERTIES_GLSL 25 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/sampler.glsl: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLER_GLSL 2 | #define SAMPLER_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | vec4 sampler1D_sample(sampler1D t, float u) { return texture(t, u); } 9 | int sampler1D_size(sampler1D t) { return textureSize(t, 0); } 10 | vec4 sampler1D_textel_fetch(sampler1D t, int u) { return texelFetch(t, u, 0); } 11 | 12 | /* META @uv: default=UV[0];*/ 13 | vec4 sampler2D_sample(sampler2D t, vec2 uv) { return texture(t, uv); } 14 | /* META @uv: default=UV[0];*/ 15 | vec4 sampler2D_sample_nearest(sampler2D t, vec2 uv){ return texelFetch(t, ivec2(textureSize(t, 0) * uv), 0); } 16 | ivec2 sampler2D_size(sampler2D t) { return textureSize(t, 0); } 17 | vec4 sampler2D_textel_fetch(sampler2D t, ivec2 uv) { return texelFetch(t, uv, 0); } 18 | 19 | #endif //SAMPLER_GLSL 20 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/vec2.glsl: -------------------------------------------------------------------------------- 1 | #ifndef VEC2_GLSL 2 | #define VEC2_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | vec2 vec2_add(vec2 a, vec2 b){ return a+b; } 9 | vec2 vec2_subtract(vec2 a, vec2 b){ return a-b; } 10 | vec2 vec2_multiply(vec2 a, vec2 b){ return a*b; } 11 | vec2 vec2_divide(vec2 a, vec2 b){ return a/b; } 12 | vec2 vec2_scale(vec2 v, float s){ return v*s; } 13 | vec2 vec2_modulo(vec2 a, vec2 b){ return mod(a,b); } 14 | vec2 vec2_pow(vec2 v, vec2 e){ return pow(v, e); } 15 | vec2 vec2_sqrt(vec2 v){ return sqrt(v); } 16 | 17 | vec2 vec2_round(vec2 v){ return round(v); } 18 | vec2 vec2_fract(vec2 v){ return fract(v); } 19 | vec2 vec2_floor(vec2 v){ return floor(v); } 20 | vec2 vec2_ceil(vec2 v){ return ceil(v); } 21 | 22 | vec2 vec2_clamp(vec2 v, vec2 min, vec2 max){ return clamp(v, min, max); } 23 | 24 | vec2 vec2_sign(vec2 v){ return sign(v); } 25 | vec2 vec2_abs(vec2 v){ return abs(v); } 26 | vec2 vec2_min(vec2 a, vec2 b){ return min(a,b); } 27 | vec2 vec2_max(vec2 a, vec2 b){ return max(a,b); } 28 | 29 | vec2 vec2_mix(vec2 a, vec2 b, vec2 factor){ return mix(a,b,factor); } 30 | vec2 vec2_mix_float(vec2 a, vec2 b, float factor){ return mix(a,b,factor); } 31 | 32 | vec2 vec2_normalize(vec2 v){ return normalize(v); } 33 | 34 | float vec2_length(vec2 v){ return length(v); } 35 | float vec2_distance(vec2 a, vec2 b){ return distance(a,b); } 36 | float vec2_dot_product(vec2 a, vec2 b){ return dot(a,b); } 37 | 38 | bool vec2_equal(vec2 a, vec2 b){ return a == b; } 39 | bool vec2_not_equal(vec2 a, vec2 b){ return a != b; } 40 | 41 | vec2 vec2_if_else(bool condition, vec2 if_true, vec2 if_false){ return condition ? if_true : if_false; } 42 | 43 | vec2 vec2_join(float x, float y) { return vec2(x,y);} 44 | void vec2_split(vec2 v, out float x, out float y){ x=v.x; y=v.y; } 45 | 46 | #endif //VEC2_GLSL 47 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/vec3.glsl: -------------------------------------------------------------------------------- 1 | #ifndef VEC3_GLSL 2 | #define VEC3_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 9 | vec3 vec3_add(vec3 a, vec3 b){ return a+b; } 10 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 11 | vec3 vec3_subtract(vec3 a, vec3 b){ return a-b; } 12 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 13 | vec3 vec3_multiply(vec3 a, vec3 b){ return a*b; } 14 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 15 | vec3 vec3_divide(vec3 a, vec3 b){ return a/b; } 16 | /*META @v: subtype=Vector;*/ 17 | vec3 vec3_scale(vec3 v, float s){ return v*s; } 18 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 19 | vec3 vec3_modulo(vec3 a, vec3 b){ return mod(a,b); } 20 | /*META @v: subtype=Vector; @e: subtype=Vector;*/ 21 | vec3 vec3_pow(vec3 v, vec3 e){ return pow(v, e); } 22 | /*META @v: subtype=Vector;*/ 23 | vec3 vec3_sqrt(vec3 v){ return sqrt(v); } 24 | 25 | /*META @v: subtype=Vector;*/ 26 | vec3 vec3_round(vec3 v){ return round(v); } 27 | /*META @v: subtype=Vector;*/ 28 | vec3 vec3_fract(vec3 v){ return fract(v); } 29 | /*META @v: subtype=Vector;*/ 30 | vec3 vec3_floor(vec3 v){ return floor(v); } 31 | /*META @v: subtype=Vector;*/ 32 | vec3 vec3_ceil(vec3 v){ return ceil(v); } 33 | 34 | /*META @v: subtype=Vector; @min: subtype=Vector; @max: subtype=Vector;*/ 35 | vec3 vec3_clamp(vec3 v, vec3 min, vec3 max){ return clamp(v, min, max); } 36 | 37 | /*META @v: subtype=Vector;*/ 38 | vec3 vec3_sign(vec3 v){ return sign(v); } 39 | /*META @v: subtype=Vector;*/ 40 | vec3 vec3_abs(vec3 v){ return abs(v); } 41 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 42 | vec3 vec3_min(vec3 a, vec3 b){ return min(a,b); } 43 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 44 | vec3 vec3_max(vec3 a, vec3 b){ return max(a,b); } 45 | 46 | /*META @a: subtype=Vector; @b: subtype=Vector; @factor: subtype=Vector;*/ 47 | vec3 vec3_mix(vec3 a, vec3 b, vec3 factor){ return mix(a,b,factor); } 48 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 49 | vec3 vec3_mix_float(vec3 a, vec3 b, float factor){ return mix(a,b,factor); } 50 | 51 | /*META @v: subtype=Vector;*/ 52 | vec3 vec3_normalize(vec3 v){ return normalize(v); } 53 | 54 | /*META @v: subtype=Vector;*/ 55 | float vec3_length(vec3 v){ return length(v); } 56 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 57 | float vec3_distance(vec3 a, vec3 b){ return distance(a,b); } 58 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 59 | float vec3_dot_product(vec3 a, vec3 b){ return dot(a,b); } 60 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 61 | vec3 vec3_cross_product(vec3 a, vec3 b){ return cross(a,b); } 62 | 63 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 64 | bool vec3_equal(vec3 a, vec3 b){ return a == b; } 65 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 66 | bool vec3_not_equal(vec3 a, vec3 b){ return a != b; } 67 | 68 | /*META @if_true: subtype=Vector; @if_false: subtype=Vector;*/ 69 | vec3 vec3_if_else(bool condition, vec3 if_true, vec3 if_false){ return condition ? if_true : if_false; } 70 | 71 | vec3 vec3_join(float x, float y, float z) { return vec3(x,y,z);} 72 | /*META @v: subtype=Vector;*/ 73 | void vec3_split(vec3 v, out float x, out float y, out float z){ x=v.x; y=v.y; z=v.z; } 74 | 75 | #endif //VEC3_GLSL 76 | -------------------------------------------------------------------------------- /Malt/Shaders/Node Utils/vec4.glsl: -------------------------------------------------------------------------------- 1 | #ifndef VEC4_GLSL 2 | #define VEC4_GLSL 3 | 4 | /* META GLOBAL 5 | @meta: internal = true; 6 | */ 7 | 8 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 9 | vec4 vec4_add(vec4 a, vec4 b){ return a+b; } 10 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 11 | vec4 vec4_subtract(vec4 a, vec4 b){ return a-b; } 12 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 13 | vec4 vec4_multiply(vec4 a, vec4 b){ return a*b; } 14 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 15 | vec4 vec4_divide(vec4 a, vec4 b){ return a/b; } 16 | /*META @v: subtype=Vector;*/ 17 | vec4 vec4_scale(vec4 v, float s){ return v*s; } 18 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 19 | vec4 vec4_modulo(vec4 a, vec4 b){ return mod(a,b); } 20 | /*META @v: subtype=Vector; @e: subtype=Vector;*/ 21 | vec4 vec4_pow(vec4 v, vec4 e){ return pow(v, e); } 22 | /*META @v: subtype=Vector;*/ 23 | vec4 vec4_sqrt(vec4 v){ return sqrt(v); } 24 | 25 | /*META @v: subtype=Vector;*/ 26 | vec4 vec4_round(vec4 v){ return round(v); } 27 | /*META @v: subtype=Vector;*/ 28 | vec4 vec4_fract(vec4 v){ return fract(v); } 29 | /*META @v: subtype=Vector;*/ 30 | vec4 vec4_floor(vec4 v){ return floor(v); } 31 | /*META @v: subtype=Vector;*/ 32 | vec4 vec4_ceil(vec4 v){ return ceil(v); } 33 | 34 | /*META @v: subtype=Vector; @min: subtype=Vector; @max: subtype=Vector;*/ 35 | vec4 vec4_clamp(vec4 v, vec4 min, vec4 max){ return clamp(v, min, max); } 36 | 37 | /*META @v: subtype=Vector;*/ 38 | vec4 vec4_sign(vec4 v){ return sign(v); } 39 | /*META @v: subtype=Vector;*/ 40 | vec4 vec4_abs(vec4 v){ return abs(v); } 41 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 42 | vec4 vec4_min(vec4 a, vec4 b){ return min(a,b); } 43 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 44 | vec4 vec4_max(vec4 a, vec4 b){ return max(a,b); } 45 | 46 | /*META @a: subtype=Vector; @b: subtype=Vector; @factor: subtype=Vector;*/ 47 | vec4 vec4_mix(vec4 a, vec4 b, vec4 factor){ return mix(a,b,factor); } 48 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 49 | vec4 vec4_mix_float(vec4 a, vec4 b, float factor){ return mix(a,b,factor); } 50 | 51 | /*META @v: subtype=Vector;*/ 52 | vec4 vec4_normalize(vec4 v){ return normalize(v); } 53 | 54 | /*META @v: subtype=Vector;*/ 55 | float vec4_length(vec4 v){ return length(v); } 56 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 57 | float vec4_distance(vec4 a, vec4 b){ return distance(a,b); } 58 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 59 | float vec4_dot_product(vec4 a, vec4 b){ return dot(a,b); } 60 | 61 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 62 | bool vec4_equal(vec4 a, vec4 b){ return a == b; } 63 | /*META @a: subtype=Vector; @b: subtype=Vector;*/ 64 | bool vec4_not_equal(vec4 a, vec4 b){ return a != b; } 65 | 66 | /*META @if_true: subtype=Vector; @if_false: subtype=Vector;*/ 67 | vec4 vec4_if_else(bool condition, vec4 if_true, vec4 if_false){ return condition ? if_true : if_false; } 68 | 69 | vec4 vec4_join(float r, float g, float b, float a) { return vec4(r,g,b,a);} 70 | /*META @v: subtype=Vector;*/ 71 | void vec4_split(vec4 v, out float r, out float g, out float b, out float a){ r=v.r; g=v.g; b=v.b; a=v.a; } 72 | 73 | #endif //VEC4_GLSL 74 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/BlendTexture.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | uniform sampler2D blend_texture; 13 | 14 | layout (location = 0) out vec4 OUT_COLOR; 15 | 16 | void main() 17 | { 18 | PIXEL_SETUP_INPUT(); 19 | 20 | vec4 color = texture(blend_texture, UV[0]); 21 | color.rgb *= color.a; 22 | OUT_COLOR = color; 23 | } 24 | 25 | #endif //PIXEL_SHADER 26 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/BlendTransparency.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | #include "Common/Color.glsl" 3 | 4 | #ifdef VERTEX_SHADER 5 | void main() 6 | { 7 | DEFAULT_SCREEN_VERTEX_SHADER(); 8 | } 9 | #endif 10 | 11 | #ifdef PIXEL_SHADER 12 | 13 | uniform sampler2D IN_BACK[8]; 14 | uniform sampler2D IN_FRONT[8]; 15 | 16 | layout (location = 0) out vec4 OUT_RESULT_0; 17 | layout (location = 1) out vec4 OUT_RESULT_1; 18 | layout (location = 2) out vec4 OUT_RESULT_2; 19 | layout (location = 3) out vec4 OUT_RESULT_3; 20 | layout (location = 4) out vec4 OUT_RESULT_4; 21 | layout (location = 5) out vec4 OUT_RESULT_5; 22 | layout (location = 6) out vec4 OUT_RESULT_6; 23 | layout (location = 7) out vec4 OUT_RESULT_7; 24 | 25 | void main() 26 | { 27 | PIXEL_SETUP_INPUT(); 28 | 29 | ivec2 uv = ivec2(gl_FragCoord.xy); 30 | 31 | OUT_RESULT_0 = alpha_blend(texelFetch(IN_BACK[0], uv, 0), texelFetch(IN_FRONT[0], uv, 0)); 32 | OUT_RESULT_1 = alpha_blend(texelFetch(IN_BACK[1], uv, 0), texelFetch(IN_FRONT[1], uv, 0)); 33 | OUT_RESULT_2 = alpha_blend(texelFetch(IN_BACK[2], uv, 0), texelFetch(IN_FRONT[2], uv, 0)); 34 | OUT_RESULT_3 = alpha_blend(texelFetch(IN_BACK[3], uv, 0), texelFetch(IN_FRONT[3], uv, 0)); 35 | OUT_RESULT_4 = alpha_blend(texelFetch(IN_BACK[4], uv, 0), texelFetch(IN_FRONT[4], uv, 0)); 36 | OUT_RESULT_5 = alpha_blend(texelFetch(IN_BACK[5], uv, 0), texelFetch(IN_FRONT[5], uv, 0)); 37 | OUT_RESULT_6 = alpha_blend(texelFetch(IN_BACK[6], uv, 0), texelFetch(IN_FRONT[6], uv, 0)); 38 | OUT_RESULT_7 = alpha_blend(texelFetch(IN_BACK[7], uv, 0), texelFetch(IN_FRONT[7], uv, 0)); 39 | } 40 | 41 | #endif //PIXEL_SHADER 42 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/CopyTextures.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | uniform sampler2D IN[8]; 13 | 14 | uniform sampler2D IN_DEPTH; 15 | 16 | layout (location = 0) out vec4 OUT_0; 17 | layout (location = 1) out vec4 OUT_1; 18 | layout (location = 2) out vec4 OUT_2; 19 | layout (location = 3) out vec4 OUT_3; 20 | layout (location = 4) out vec4 OUT_4; 21 | layout (location = 5) out vec4 OUT_5; 22 | layout (location = 6) out vec4 OUT_6; 23 | layout (location = 7) out vec4 OUT_7; 24 | 25 | void main() 26 | { 27 | PIXEL_SETUP_INPUT(); 28 | 29 | ivec2 uv = ivec2(gl_FragCoord.xy); 30 | 31 | OUT_0 = texelFetch(IN[0], uv, 0); 32 | OUT_1 = texelFetch(IN[1], uv, 0); 33 | OUT_2 = texelFetch(IN[2], uv, 0); 34 | OUT_3 = texelFetch(IN[3], uv, 0); 35 | OUT_4 = texelFetch(IN[4], uv, 0); 36 | OUT_5 = texelFetch(IN[5], uv, 0); 37 | OUT_6 = texelFetch(IN[6], uv, 0); 38 | OUT_7 = texelFetch(IN[7], uv, 0); 39 | 40 | gl_FragDepth = texelFetch(IN_DEPTH, uv, 0).x; 41 | } 42 | 43 | #endif //PIXEL_SHADER 44 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/DepthToBlenderDepth.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | uniform sampler2D DEPTH_TEXTURE; 13 | uniform int DEPTH_CHANNEL; 14 | 15 | layout (location = 0) out vec4 OUT_RESULT; 16 | 17 | void main() 18 | { 19 | PIXEL_SETUP_INPUT(); 20 | 21 | float depth = texture(DEPTH_TEXTURE, UV[0])[DEPTH_CHANNEL]; 22 | vec3 camera = screen_to_camera(UV[0], depth); 23 | OUT_RESULT.r = -camera.z; 24 | } 25 | 26 | #endif //PIXEL_SHADER 27 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/JumpFlood.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | #include "Filters/JumpFlood.glsl" 13 | 14 | uniform sampler2D input_texture; 15 | uniform float width; 16 | 17 | layout (location = 0) out vec4 OUT_RESULT; 18 | 19 | void main() 20 | { 21 | PIXEL_SETUP_INPUT(); 22 | 23 | vec2 uv = screen_uv(); 24 | OUT_RESULT = jump_flood(input_texture, uv, width, true); 25 | } 26 | 27 | #endif //PIXEL_SHADER 28 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/LineComposite.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | #include "Filters/Line.glsl" 13 | 14 | layout (location = 0) out vec4 OUT_RESULT; 15 | 16 | uniform sampler2D color_texture; 17 | 18 | uniform sampler2D depth_texture; 19 | uniform int depth_channel; 20 | 21 | uniform usampler2D id_texture; 22 | uniform int id_channel; 23 | 24 | uniform sampler2D line_color_texture; 25 | 26 | uniform sampler2D line_width_texture; 27 | uniform int line_width_channel; 28 | uniform float line_width_scale = 1.0; 29 | 30 | uniform int brute_force_range = 10; 31 | 32 | void main() 33 | { 34 | PIXEL_SETUP_INPUT(); 35 | 36 | vec2 uv = UV[0]; 37 | vec4 line_color = line_expand( 38 | uv, brute_force_range, 39 | line_color_texture, line_width_texture, line_width_channel, line_width_scale, 40 | depth_texture, depth_channel, id_texture, id_channel 41 | ).color; 42 | 43 | vec4 color = texture(color_texture, uv); 44 | 45 | OUT_RESULT = alpha_blend(color, line_color); 46 | } 47 | 48 | #endif //PIXEL_SHADER 49 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/Unpack8bitTextures.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | uniform usampler2D IN_PACKED; 13 | 14 | layout (location = 0) out vec4 OUT_A; 15 | layout (location = 1) out vec4 OUT_B; 16 | layout (location = 2) out vec4 OUT_C; 17 | layout (location = 3) out vec4 OUT_D; 18 | 19 | void main() 20 | { 21 | PIXEL_SETUP_INPUT(); 22 | 23 | uvec4 packed_pixel = texture(IN_PACKED, UV[0]); 24 | OUT_A = unpackUnorm4x8(packed_pixel.r); 25 | OUT_B = unpackUnorm4x8(packed_pixel.g); 26 | OUT_C = unpackUnorm4x8(packed_pixel.b); 27 | OUT_D = unpackUnorm4x8(packed_pixel.a); 28 | } 29 | 30 | #endif //PIXEL_SHADER 31 | -------------------------------------------------------------------------------- /Malt/Shaders/Passes/sRGBConversion.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | 3 | #ifdef VERTEX_SHADER 4 | void main() 5 | { 6 | DEFAULT_SCREEN_VERTEX_SHADER(); 7 | } 8 | #endif 9 | 10 | #ifdef PIXEL_SHADER 11 | 12 | uniform sampler2D input_texture; 13 | uniform bool to_srgb; 14 | uniform bool convert = true; 15 | 16 | layout (location = 0) out vec4 OUT_COLOR; 17 | 18 | void main() 19 | { 20 | PIXEL_SETUP_INPUT(); 21 | 22 | vec4 color = texture(input_texture, UV[0]); 23 | if(convert) 24 | { 25 | if(to_srgb) 26 | { 27 | color.rgb = linear_to_srgb(color.rgb); 28 | } 29 | else 30 | { 31 | color.rgb = srgb_to_linear(color.rgb); 32 | } 33 | } 34 | OUT_COLOR = color; 35 | } 36 | 37 | #endif //PIXEL_SHADER 38 | -------------------------------------------------------------------------------- /Malt/Shaders/Procedural/Bayer.glsl: -------------------------------------------------------------------------------- 1 | #ifndef BAYER_GLSL 2 | #define BAYER_GLSL 3 | 4 | // Based on: https://en.wikipedia.org/wiki/Ordered_dithering 5 | 6 | /* META GLOBAL 7 | @meta: internal=true; 8 | */ 9 | 10 | int _bayer_index(ivec2 texel, int size) 11 | { 12 | texel = texel % ivec2(size); 13 | return texel.x + size*texel.y; 14 | } 15 | 16 | float bayer_2x2(ivec2 texel) 17 | { 18 | int matrix[4] = int[4]( 19 | 0,2, 20 | 3,1 21 | ); 22 | return float(matrix[_bayer_index(texel, 2)]) / 4.0; 23 | } 24 | 25 | float bayer_3x3(ivec2 texel) 26 | { 27 | int matrix[9] = int[9]( 28 | 0,7,3, 29 | 6,5,2, 30 | 4,1,8 31 | ); 32 | return float(matrix[_bayer_index(texel, 3)]) / 9.0; 33 | } 34 | 35 | float bayer_4x4(ivec2 texel) 36 | { 37 | int matrix[16] = int[16]( 38 | 0,8,2,10, 39 | 12,4,14,6, 40 | 3,11,1,9, 41 | 15,7,13,5 42 | ); 43 | return float(matrix[_bayer_index(texel, 4)]) / 16.0; 44 | } 45 | 46 | float bayer_8x8(ivec2 texel) 47 | { 48 | int matrix[64] = int[64]( 49 | 0,32,8,40,2,34,10,42, 50 | 48,16,56,24,50,18,58,26, 51 | 12,44,4,36,14,46,6,38, 52 | 60,28,53,20,62,30,54,22, 53 | 3,35,11,43,1,33,9,41, 54 | 51,19,59,27,49,17,57,25, 55 | 15,47,7,39,13,45,5,37, 56 | 63,31,55,23,61,29,53,21 57 | ); 58 | return float(matrix[_bayer_index(texel, 8)]) / 64.0; 59 | } 60 | 61 | #endif //BAYER_GLSL 62 | -------------------------------------------------------------------------------- /Malt/Shaders/Procedural/Cell_Noise.inl: -------------------------------------------------------------------------------- 1 | //Should define T, DIMENSIONS and TILE 2 | //Should declare coord and tile_size (if TILE) 3 | T real_coord = coord; 4 | #if TILE != 0 5 | T _tile_size = round(T(tile_size)); 6 | coord = mod(coord, _tile_size); 7 | #endif 8 | T real_origin = floor(real_coord) + 0.5; 9 | T origin = floor(coord) + 0.5; 10 | T delta = fract(coord); 11 | 12 | vec4 r_cell_color; 13 | T r_cell_position; 14 | float r_cell_distance = 10; 15 | 16 | #if DIMENSIONS == 4 17 | const ivec4 D = ivec4(1); 18 | #elif DIMENSIONS == 3 19 | const ivec4 D = ivec4(1,1,1,0); 20 | #elif DIMENSIONS == 2 21 | const ivec4 D = ivec4(1,1,0,0); 22 | #elif DIMENSIONS == 1 23 | const ivec4 D = ivec4(1,0,0,0); 24 | #endif 25 | 26 | for(int w = -D.w; w <= D.w; w++) 27 | { 28 | for(int z = -D.z; z <= D.z; z++) 29 | { 30 | for(int y = -D.y; y <= D.y; y++) 31 | { 32 | for(int x = -D.x; x <= D.x; x++) 33 | { 34 | T offset = T(vec4(x,y,z,w)); 35 | T real_corner = real_origin + offset; 36 | T corner = origin + offset; 37 | #if TILE != 0 38 | corner = mod(corner, _tile_size); 39 | #endif 40 | vec4 corner_hash = hash(corner); 41 | T real_cell_position = real_corner + (T(corner_hash) - 0.5); 42 | T cell_position = corner + (T(corner_hash) - 0.5); 43 | float cell_distance = distance(real_coord, real_cell_position); 44 | if(cell_distance < r_cell_distance) 45 | { 46 | r_cell_color = corner_hash; 47 | r_cell_position = real_cell_position; 48 | r_cell_distance = cell_distance; 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Malt/Shaders/Procedural/Fractal_Noise.glsl: -------------------------------------------------------------------------------- 1 | #ifndef PROCEDURAL_FRACTAL_NOISE_GLSL 2 | #define PROCEDURAL_FRACTAL_NOISE_GLSL 3 | 4 | #include "Noise.glsl" 5 | 6 | /* META GLOBAL 7 | @meta: category=Texturing; @meta: internal=true; 8 | */ 9 | 10 | /* META 11 | @coord: subtype=Vector; default=vec4(POSITION,0); 12 | @detail: default=3.0; min=1.0; 13 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 14 | @tile_size: default=ivec4(5); 15 | */ 16 | vec4 fractal_noise(vec4 coord, float detail, float detail_balance, ivec4 tile_size) 17 | { 18 | #define TILE 1 19 | #include "Fractal_Noise.inl" 20 | return color; 21 | } 22 | 23 | /* META 24 | @coord: subtype=Vector; default=vec4(POSITION,0); 25 | @detail: default=3.0; min=1.0; 26 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 27 | */ 28 | vec4 fractal_noise(vec4 coord, float detail, float detail_balance) 29 | { 30 | #define TILE 0 31 | #include "Fractal_Noise.inl" 32 | return color; 33 | } 34 | 35 | /* META 36 | @coord: subtype=Vector; default=POSITION; 37 | @detail: default=3.0; min=1.0; 38 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 39 | @tile_size: default=ivec3(5); 40 | */ 41 | vec4 fractal_noise(vec3 coord, float detail, float detail_balance, ivec3 tile_size) 42 | { 43 | #define TILE 1 44 | #include "Fractal_Noise.inl" 45 | return color; 46 | } 47 | 48 | /* META 49 | @coord: subtype=Vector; default=POSITION; 50 | @detail: default=3.0; min=1.0; 51 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 52 | */ 53 | vec4 fractal_noise(vec3 coord, float detail, float detail_balance) 54 | { 55 | #define TILE 0 56 | #include "Fractal_Noise.inl" 57 | return color; 58 | } 59 | 60 | /* META 61 | @coord: subtype=Vector; default=POSITION.xy; 62 | @detail: default=3.0; min=1.0; 63 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 64 | @tile_size: default=ivec2(5); 65 | */ 66 | vec4 fractal_noise(vec2 coord, float detail, float detail_balance, ivec2 tile_size) 67 | { 68 | #define TILE 1 69 | #include "Fractal_Noise.inl" 70 | return color; 71 | } 72 | 73 | /* META 74 | @coord: subtype=Vector; default=POSITION.xy; 75 | @detail: default=3.0; min=1.0; 76 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 77 | */ 78 | vec4 fractal_noise(vec2 coord, float detail, float detail_balance) 79 | { 80 | #define TILE 0 81 | #include "Fractal_Noise.inl" 82 | return color; 83 | } 84 | 85 | /* META 86 | @coord: subtype=Vector; default=POSITION.x; 87 | @detail: default=3.0; min=1.0; 88 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 89 | @tile_size: default=5; 90 | */ 91 | vec4 fractal_noise(float coord, float detail, float detail_balance, int tile_size) 92 | { 93 | #define TILE 1 94 | #include "Fractal_Noise.inl" 95 | return color; 96 | } 97 | 98 | /* META 99 | @coord: subtype=Vector; default=POSITION.x; 100 | @detail: default=3.0; min=1.0; 101 | @detail_balance: subtype=Slider; min=0.0; max=1.0; default = 0.5; 102 | */ 103 | vec4 fractal_noise(float coord, float detail, float detail_balance) 104 | { 105 | #define TILE 0 106 | #include "Fractal_Noise.inl" 107 | return color; 108 | } 109 | 110 | #undef TILE 111 | 112 | // Keep for backward compatibility 113 | vec4 fractal_noise_ex(vec4 coord, int octaves, bool tile, vec4 tile_size) 114 | { 115 | if(tile) 116 | { 117 | return fractal_noise(coord, float(octaves), 0.5, ivec4(tile_size)); 118 | } 119 | else 120 | { 121 | return fractal_noise(coord, float(octaves), 0.5); 122 | } 123 | } 124 | vec4 fractal_noise(vec4 coord, int octaves) 125 | { 126 | return fractal_noise(coord, float(octaves), 0.5); 127 | } 128 | 129 | 130 | #endif // PROCEDURAL_FRACTAL_NOISE_GLSL 131 | -------------------------------------------------------------------------------- /Malt/Shaders/Procedural/Fractal_Noise.inl: -------------------------------------------------------------------------------- 1 | //Should define TILE 2 | //Should declare coord and tile_size (if TILE) 3 | 4 | vec4 color = vec4(0); 5 | float weight = 1.0; 6 | float total_weight = 0.0; 7 | detail_balance = clamp(detail_balance, 0.00001, 1.0); 8 | detail = max(1.0, detail); 9 | 10 | for (int i = 0; i < ceil(detail); i++) 11 | { 12 | #if TILE != 0 13 | vec4 noise = noise(coord, tile_size); 14 | #else 15 | vec4 noise = noise(coord); 16 | #endif 17 | float octave_weight = (i + 1 > floor(detail))? mod(detail, 1.0) : 1.0; 18 | weight *= detail_balance * 2 * octave_weight; 19 | color += weight * noise; 20 | total_weight += weight; 21 | coord *= 2.0; 22 | #if TILE != 0 23 | tile_size *= 2; 24 | #endif 25 | } 26 | 27 | color /= total_weight; -------------------------------------------------------------------------------- /Malt/Shaders/Procedural/Noise.glsl: -------------------------------------------------------------------------------- 1 | #ifndef PROCEDURAL_NOISE_GLSL 2 | #define PROCEDURAL_NOISE_GLSL 3 | 4 | #include "Common/Hash.glsl" 5 | 6 | /* META GLOBAL 7 | @meta: internal=true; 8 | */ 9 | 10 | /* META 11 | @coord: subtype=Vector; default=vec4(POSITION,0); 12 | @tile_size: subtype=Vector; default=vec4(1); 13 | */ 14 | vec4 noise_ex(vec4 coord, bool tile, vec4 tile_size) 15 | { 16 | //Kept for backward compatibility 17 | #define DIMENSIONS 4 18 | #define T vec4 19 | if(tile) 20 | { 21 | #define TILE 1 22 | #include "Noise.inl" 23 | return color; 24 | } 25 | else 26 | { 27 | #define TILE 0 28 | #include "Noise.inl" 29 | return color; 30 | } 31 | } 32 | 33 | /* META 34 | @coord: subtype=Vector; default=vec4(POSITION,0); 35 | @tile_size: subtype=Vector; default=ivec4(5); 36 | */ 37 | vec4 noise(vec4 coord, ivec4 tile_size) 38 | { 39 | #define DIMENSIONS 4 40 | #define T vec4 41 | #define TILE 1 42 | #include "Noise.inl" 43 | return color; 44 | } 45 | 46 | /* META 47 | @coord: subtype=Vector; default=vec4(POSITION,0); 48 | */ 49 | vec4 noise(vec4 coord) 50 | { 51 | #define DIMENSIONS 4 52 | #define T vec4 53 | #define TILE 0 54 | #include "Noise.inl" 55 | return color; 56 | } 57 | 58 | /* META 59 | @coord: subtype=Vector; default=POSITION; 60 | @tile_size: subtype=Vector; default=ivec3(5); 61 | */ 62 | vec4 noise(vec3 coord, ivec3 tile_size) 63 | { 64 | #define DIMENSIONS 3 65 | #define T vec3 66 | #define TILE 1 67 | #include "Noise.inl" 68 | return color; 69 | } 70 | 71 | /* META 72 | @coord: subtype=Vector; default=POSITION; 73 | */ 74 | vec4 noise(vec3 coord) 75 | { 76 | #define DIMENSIONS 3 77 | #define T vec3 78 | #define TILE 0 79 | #include "Noise.inl" 80 | return color; 81 | } 82 | 83 | /* META 84 | @coord: subtype=Vector; default=POSITION.xy; 85 | @tile_size: subtype=Vector; default=ivec2(5); 86 | */ 87 | vec4 noise(vec2 coord, ivec2 tile_size) 88 | { 89 | #define DIMENSIONS 2 90 | #define T vec2 91 | #define TILE 1 92 | #include "Noise.inl" 93 | return color; 94 | } 95 | 96 | /* META 97 | @coord: subtype=Vector; default=POSITION.xy; 98 | */ 99 | vec4 noise(vec2 coord) 100 | { 101 | #define DIMENSIONS 2 102 | #define T vec2 103 | #define TILE 0 104 | #include "Noise.inl" 105 | return color; 106 | } 107 | 108 | /* META 109 | @coord: subtype=Vector; default=POSITION.x; 110 | @tile_size: subtype=Vector; default=5; 111 | */ 112 | vec4 noise(float coord, int tile_size) 113 | { 114 | #define DIMENSIONS 1 115 | #define T float 116 | #define TILE 1 117 | #include "Noise.inl" 118 | return color; 119 | } 120 | 121 | /* META 122 | @coord: subtype=Vector; default=POSITION.x; 123 | */ 124 | vec4 noise(float coord) 125 | { 126 | #define DIMENSIONS 1 127 | #define T float 128 | #define TILE 0 129 | #include "Noise.inl" 130 | return color; 131 | } 132 | 133 | #undef DIMENSIONS 134 | #undef T 135 | #undef TILE 136 | 137 | #endif // PROCEDURAL_NOISE_GLSL 138 | -------------------------------------------------------------------------------- /Malt/Shaders/Procedural/Noise.inl: -------------------------------------------------------------------------------- 1 | //Should define T, DIMENSIONS and TILE 2 | //Should declare coord and tile_size (if TILE) 3 | #if TILE != 0 4 | T _tile_size = round(tile_size); 5 | coord = mod(coord, _tile_size); 6 | #endif 7 | T origin = floor(coord); 8 | T delta = fract(coord); 9 | //quintic interpolation factor 10 | T factor = delta*delta*delta*(delta*(delta*6.0-15.0)+10.0); 11 | 12 | #if DIMENSIONS == 4 13 | const ivec4 D = ivec4(1); 14 | #elif DIMENSIONS == 3 15 | const ivec4 D = ivec4(1,1,1,0); 16 | #elif DIMENSIONS == 2 17 | const ivec4 D = ivec4(1,1,0,0); 18 | #elif DIMENSIONS == 1 19 | const ivec4 D = ivec4(1,0,0,0); 20 | #endif 21 | 22 | vec4 w_results[2]; 23 | vec4 z_results[2]; 24 | vec4 y_results[2]; 25 | vec4 x_results[2]; 26 | 27 | for(int w = 0; w <= D.w; w++) 28 | { 29 | for(int z = 0; z <= D.z; z++) 30 | { 31 | for(int y = 0; y <= D.y; y++) 32 | { 33 | for(int x = 0; x <= D.x; x++) 34 | { 35 | T offset = T(vec4(x,y,z,w)); 36 | T corner = origin + offset; 37 | #if TILE != 0 38 | corner = mod(corner, _tile_size); 39 | #endif 40 | T corner_hash_r = T(hash(corner)) * 2.0 - 1.0; //(-1|+1) range 41 | T corner_hash_g = T(hash(corner_hash_r)) * 2.0 - 1.0; 42 | T corner_hash_b = T(hash(corner_hash_g)) * 2.0 - 1.0; 43 | T corner_hash_a = T(hash(corner_hash_b)) * 2.0 - 1.0; 44 | x_results[x].r = dot(corner_hash_r, delta-offset); 45 | x_results[x].g = dot(corner_hash_g, delta-offset); 46 | x_results[x].b = dot(corner_hash_b, delta-offset); 47 | x_results[x].a = dot(corner_hash_a, delta-offset); 48 | } 49 | #if DIMENSIONS >= 2 50 | y_results[y] = mix(x_results[0], x_results[1], factor.x); 51 | #endif 52 | } 53 | #if DIMENSIONS >= 3 54 | z_results[z] = mix(y_results[0], y_results[1], factor.y); 55 | #endif 56 | } 57 | #if DIMENSIONS >= 4 58 | w_results[w] = mix(z_results[0], z_results[1], factor.z); 59 | #endif 60 | } 61 | 62 | vec4 color; 63 | #if DIMENSIONS == 4 64 | color = mix(w_results[0], w_results[1], factor.w) * 0.5 + 0.5; 65 | #elif DIMENSIONS == 3 66 | color = mix(z_results[0], z_results[1], factor.z) * 0.5 + 0.5; 67 | #elif DIMENSIONS == 2 68 | color = mix(y_results[0], y_results[1], factor.y) * 0.5 + 0.5; 69 | #elif DIMENSIONS == 1 70 | color = mix(x_results[0], x_results[1], factor) * 0.5 + 0.5; 71 | #endif 72 | 73 | -------------------------------------------------------------------------------- /Malt/Shaders/readme.md: -------------------------------------------------------------------------------- 1 | # Shader Library 2 | 3 | All *GLSL* code should go here. 4 | All shader files have their own [*include guard*](https://en.wikipedia.org/wiki/Include_guard) macro. 5 | *Include guards* are used instead of the *#pragma once* preprocessor directive to allow user code to override the Malt built-in code when needed. 6 | 7 | Whenever it makes sense, functions should be implemented in a pipeline/renderer agnostic way. 8 | Ideally it should be possible to copy-paste *Malt* functions into any other render engine with *GLSL* support. 9 | 10 | [Common.glsl](Common.glsl) contains all the code assumed to be shared by all pipelines, including the vertex attributes layout, the *COMMON_UNIFORMS* block and the uniform blocks needed for batch rendering. 11 | 12 | The [Common](Common) folder contains 3d math and rendering utility functions. 13 | 14 | [Intellisense](Intellisense) is autogenerated from a script ([build_intellisense_glsl.py](../../scripts/build_intellisense_glsl.py)) and enables *GLSL* autocompletion to work through *C++* autocompletion providers. It's just for code editors and has no use at runtime. 15 | 16 | [Lighting](Lighting) contains a basic lighting and shadow mapping implementation and it's intended to be extended by custom pipelines. 17 | 18 | [Shading](Shading) has an extensive collection of basic building blocks for implementing *BRDFs*. 19 | 20 | [Filters](Filters) is for texture processing functions, from *Blur* to *AO*. 21 | Standalone texture processing and other types of shaders that wouldn't benefit from a library-like interface are located in [Passes](Passes) 22 | 23 | Any pipeline specific code should go inside their own folder in [Pipelines](Pipelines). 24 | -------------------------------------------------------------------------------- /Malt/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Malt 2 | 3 | Malt is a fully customizable real-time rendering framework for animation and illustration. 4 | 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. 5 | 6 | [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) 7 | 8 | ## Features 9 | 10 | - **Free and Open Source**. MIT License. 11 | - **Real Time Rendering**. 12 | - **Complete *Blender* integration**. 13 | - **Built-in Pipeline for Stylized Non Photorealistic Rendering**. 14 | - **Code as a First Class Citizen** 15 | - Automatic reloading. 16 | - *VSCode* integration, including *GLSL* autocompletion. 17 | - Automatic generation of nodes from *GLSL* functions. 18 | - Automatic UI for *Shader* and *Pipeline* parameters. 19 | - 100% customizable *Python* Render Pipelines. 20 | 21 | ## Requirements 22 | 23 | - OpenGL 4.5 24 | - Latest Blender stable release. 25 | - Windows or Linux 26 | 27 | > A dedicated Nvidia or AMD graphics card is highly recomended. 28 | 29 | ## Install 30 | 31 | - Go to [the latest Release page](https://github.com/bnpr/Malt/releases/tag/Release-latest). 32 | - Download the *BlenderMalt* version that matches your OS. 33 | - Open Blender. Go to *Preferences > Addons*, click on the *Install...* button and select *BlenderMalt.zip* from your downloads. *(It will take a few seconds)* 34 | - Tick the box in the *BlenderMalt* panel to enable it. 35 | 36 | > Altenatively, you can download the [Development version](https://github.com/bnpr/Malt/releases/tag/Development-latest) to test the latest features. 37 | 38 | ## Uninstall 39 | 40 | - Untick the box in *Preferences > Addons > BlenderMalt* to disable the addon. 41 | - Restart *Blender*. 42 | - Go back to *Preferences > Addons > BlenderMalt*, expand the panel and click the *Remove* button. 43 | 44 | ## First steps 45 | 46 | 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). 47 | The [Q&A section](https://github.com/bnpr/Malt/discussions/categories/q-a) is full of info as well. 48 | 49 | ## Developer Documentation 50 | 51 | [How to setup BlenderMalt for Development.](docs/Setup-BlenderMalt-for-Development.md) 52 | 53 | Developer documentation is best viewed directly in [Github](https://github.com/bnpr/Malt/tree/Development#developer-documentation), most folders in the source code have relevant documentation. 54 | The [Malt folder documentation](Malt#malt) is a good starting point. 55 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # Some people are used to download the zipped repo from Github to install Blender addons. 2 | # This file is just to redirect those users to the right path. It's not distributed in the actual addon. 3 | 4 | bl_info = { 5 | "name": "Oops! You downloaded the wrong BlenderMalt file.", 6 | "description" : "Please, read the install intructions on Github or malt3d.com", 7 | "author" : "Miguel Pozo", 8 | "version": (1,0,0), 9 | "blender" : (3, 0, 0), 10 | "doc_url": "https://malt3d.com/documentation/getting started/#install", 11 | "tracker_url": "https://github.com/bnpr/Malt#install", 12 | "category": "Render" 13 | } 14 | 15 | def register(): 16 | pass 17 | 18 | def unregister(): 19 | pass 20 | -------------------------------------------------------------------------------- /docs/Documentation/2022-02-16-16-45-47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-16-16-45-47.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-16-17-05-31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-16-17-05-31.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-16-17-08-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-16-17-08-23.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-16-17-20-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-16-17-20-18.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-23-16-58-56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-23-16-58-56.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-23-17-03-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-23-17-03-18.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-23-17-11-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-23-17-11-03.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-25-19-56-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-25-19-56-34.png -------------------------------------------------------------------------------- /docs/Documentation/2022-02-25-20-18-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-02-25-20-18-44.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-15-36-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-15-36-42.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-15-58-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-15-58-04.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-18-05-53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-18-05-53.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-18-11-27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-18-11-27.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-18-12-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-18-12-34.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-18-17-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-18-17-30.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-19-27-33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-19-27-33.png -------------------------------------------------------------------------------- /docs/Documentation/2022-03-21-19-36-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-03-21-19-36-40.png -------------------------------------------------------------------------------- /docs/Documentation/2022-04-02-17-34-35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-04-02-17-34-35.png -------------------------------------------------------------------------------- /docs/Documentation/2022-04-02-17-35-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-04-02-17-35-20.png -------------------------------------------------------------------------------- /docs/Documentation/2022-04-02-17-36-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-04-02-17-36-17.png -------------------------------------------------------------------------------- /docs/Documentation/2022-04-02-18-02-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/2022-04-02-18-02-24.png -------------------------------------------------------------------------------- /docs/Documentation/Getting Started.md: -------------------------------------------------------------------------------- 1 | # Gettting Started 2 | 3 | ## Requirements 4 | 5 | - OpenGL 4.5 support. 6 | - Latest Blender stable release. 7 | 8 | > A dedicated Nvidia or AMD graphics card is highly recomended. 9 | 10 | ## Install 11 | 12 | - Go to [the latest Release page](https://github.com/bnpr/Malt/releases/tag/Release-latest). 13 | - Download the *BlenderMalt* version that matches your OS. 14 | - Open Blender. Go to *Preferences > Addons*, click on the *Install...* button and select *BlenderMalt.zip* from your downloads. *(This will take a few seconds)* 15 | - Tick the box in the *BlenderMalt* panel to enable it. 16 | 17 | > Altenatively, you can download the [Development version](https://github.com/bnpr/Malt/releases/tag/Development-latest) to test the latest features. 18 | 19 | ## Uninstall 20 | 21 | - Untick the box in *Preferences > Addons > BlenderMalt* to disable the addon. 22 | - Restart *Blender*. 23 | - Go back to *Preferences > Addons > BlenderMalt*, expand the panel and click the *Remove* button. 24 | 25 | ## Enable Malt 26 | 27 | ![img](2022-02-16-16-45-47.png) 28 | 29 | *Malt* is a separate render engine, just like *Cycles* and *EEVEE*. 30 | To enable it, select *Malt* in *Properties Panel > Render Properties > Render Engine*. 31 | 32 | > When *Malt* is enabled, a tiny black window will pop up. This is the process where the renderer runs. 33 | ![img2](2022-02-16-17-20-18.png) 34 | Feel free to ignore it, it's only there because hiding it can lower the process priority and impact the render performance. 35 | 36 | ## Sample Files 37 | 38 | Sample files can be found at [Github](https://github.com/bnpr/Malt/discussions/94). 39 | 40 | ![](2022-03-21-15-58-04.png) 41 | 42 | ## Pipelines 43 | 44 | *Malt* allows implementing custom *render pipelines* for advanced use cases. 45 | 46 | However, the built-in *NPR Pipeline* is a fully featured and highly customizable pipeline designed to cover most use cases. 47 | 48 | Most parts of this documentation apply only to the *NPR Pipeline*. 49 | 50 | > For building custom *render pipelines*, see the [Developer Documentation](https://github.com/bnpr/Malt/tree/Development/Malt#malt). 51 | -------------------------------------------------------------------------------- /docs/Documentation/Plugins.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Render pipelines can be customized through plugins. 4 | Plugins can: 5 | 6 | - Add new node libraries to *Pipeline Graphs*. 7 | - Add new *Pipeline Parameters*. 8 | - Add new *PipelineGraph types*. 9 | 10 | Plugins can be installed globally in the *Addon Settings* or per world in the *World Panel*. 11 | 12 | A basic example can be found in [Development/plugins](https://github.com/bnpr/Malt/tree/Development/plugins) 13 | 14 | > The Plugins path should point to the plugins parent folder, then any plugin that you drop into that folder should be automatically loaded. 15 | 16 | > For example, the Plugins path shouldn't point to `C:/Malt-plugins/My-plugin-example/`, it should point to `C:/Malt-plugins/` 17 | -------------------------------------------------------------------------------- /docs/Documentation/Tooling.md: -------------------------------------------------------------------------------- 1 | # External Tools 2 | 3 | ## VSCode 4 | 5 | *Malt* has built-in integration with [VSCode](https://code.visualstudio.com), including a workaround to provide GLSL intellisense via C++ language servers. 6 | When *Malt* is the active render engine, it will auto-setup a *VSCode* workspace on the same folder you save your *.blend* file. 7 | >*(This feature can be disabled in the Addon Preferences)* 8 | 9 | 10 |
11 | 13 |
14 | 15 | ## RenderDoc 16 | 17 | *Malt* has built-in integration with [RenderDoc](https://renderdoc.org). 18 | It can be enabled by setting path to the **renderdoccmd** executable in the addon settings. 19 | Onnce Malt has been initialized, RenderDoc can be attached to Malt through the [attach to running instance](https://renderdoc.org/docs/window/capture_connection.html) option. 20 | 21 | ![](2022-04-02-18-02-24.png) 22 | 23 | Captures can be triggered directly through the Blender UI: 24 | ![](2022-03-21-15-36-42.png) 25 | -------------------------------------------------------------------------------- /docs/Documentation/parameter-override.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/Documentation/parameter-override.gif -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | # What's the difference between Malt and BEER? 4 | 5 | | | Malt | BEER | 6 | |---|------|------| 7 | | **Target Audience** | Advanced users | Everyone | 8 | | **Worflow** | Code & Nodes | Layers/Stacks | 9 | | **Backend** | OpenGL | Malt | 10 | | **Project Lead** | [Miguel Pozo](https://twitter.com/pragma37) | [LightBWK](https://twitter.com/Lightbwk) | 11 | | **Main Developer** | [Miguel Pozo](https://twitter.com/pragma37) | *TBD* | 12 | | **Funding** | [Patreon](https://patreon.com/pragma37) | [BEER Dev Fund](https://blendernpr.org/beer/) | 13 | 14 | # Will Malt be ported to Vulkan? 15 | 16 | The most likely replacement for *OpenGL* in *Malt* is WebGPU Native, since it's easier to use than *Vulkan* and has better *MacOS* support. 17 | 18 | # Can Malt be used for games? 19 | 20 | Not at the moment. 21 | Malt main focus is animation and illustration and it prioritizes flexibility and image quality, which usually comes at a performance cost. 22 | 23 | # How can I help? 24 | 25 | - Share your *Malt* artworks! [*#malt3d*](https://twitter.com/hashtag/malt3d) 26 | - Make tutorials. 27 | - Implement new features. 28 | - Join the [Patreon](https://www.patreon.com/pragma37). 29 | 30 | -------------------------------------------------------------------------------- /docs/From-Nodes-To-Code/all-together.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/From-Nodes-To-Code/all-together.png -------------------------------------------------------------------------------- /docs/From-Nodes-To-Code/flat-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/From-Nodes-To-Code/flat-color.png -------------------------------------------------------------------------------- /docs/From-Nodes-To-Code/lighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/From-Nodes-To-Code/lighting.png -------------------------------------------------------------------------------- /docs/From-Nodes-To-Code/starting-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/From-Nodes-To-Code/starting-point.png -------------------------------------------------------------------------------- /docs/From-Nodes-To-Code/textures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/From-Nodes-To-Code/textures.png -------------------------------------------------------------------------------- /docs/Setup-BlenderMalt-for-Development.md: -------------------------------------------------------------------------------- 1 | # How to Setup BlenderMalt for Development 2 | 3 | - Clone the repository. 4 | `git clone https://github.com/bnpr/Malt.git` 5 | - Set a user scripts folder for Blender if you don't have one. 6 | [*Blender > Preferences > File Paths > Scripts*](https://docs.blender.org/manual/en/latest/editors/preferences/file_paths.html) 7 | - Locate the *Python* executable from your *Blender* installation. 8 | For example: ```"C:\Program Files\Blender Foundation\Blender 2.93\2.93\python\bin\python.exe"``` 9 | - Run: ``` /scripts/setup_blender_addon.py --scripts-folder ``` 10 | 11 | > The setup script will try to compile the CBlenderMalt and the Bridge ipc libraries using CMake, 12 | > if you don't have the required toolchain you can just copy them from the Github release. 13 | 14 | Now if you restart *Blender* and go to *Preferences > Add-ons*, you should be able to enable *BlenderMalt*. 15 | 16 | -------------------------------------------------------------------------------- /docs/extra/extra.css: -------------------------------------------------------------------------------- 1 | /* https://github.com/squidfunk/mkdocs-material/issues/1635#issuecomment-811058692 */ 2 | 3 | [data-md-color-scheme="slate"] { 4 | --md-default-bg-color: #24272e; 5 | } 6 | 7 | /*Disable the edit button*/ 8 | .md-content__button { 9 | display: none; 10 | } 11 | 12 | .zoom { 13 | /*transition: transform ease-in-out 0.5s;*/ 14 | cursor: zoom-in; 15 | } 16 | 17 | .image-zoom-large { 18 | width: auto; 19 | max-width: 100vw; 20 | max-height: 100vh; 21 | /*transform: scale(1.5);*/ 22 | cursor: zoom-out; 23 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 24 | z-index: 100; 25 | position: absolute; 26 | left: 50%; 27 | transform: translate(-50%, 0); 28 | } -------------------------------------------------------------------------------- /docs/extra/extra.js: -------------------------------------------------------------------------------- 1 | /* https://github.com/squidfunk/mkdocs-material/issues/1635#issuecomment-811058692 */ 2 | 3 | document.querySelectorAll('.zoom').forEach(item => { 4 | item.addEventListener('click', function () { 5 | this.classList.toggle('image-zoom-large'); 6 | }) 7 | }); -------------------------------------------------------------------------------- /docs/images/become_a_patron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/images/become_a_patron.png -------------------------------------------------------------------------------- /docs/images/creativeshrimp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/images/creativeshrimp_logo.png -------------------------------------------------------------------------------- /docs/images/cube-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | #- toc 4 | #- navigation 5 | --- 6 | 7 | # Malt 8 | 9 | 10 | ***Malt*** is a fully ***customizable real-time rendering*** framework for animation and illustration. 11 | It's aimed at 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***. 12 | 13 | Designed as a community effort to expand the possibility space of 3d rendering, it provides graphics programmers and technical artist an enjoyable *“shadertoy-like”* workflow inside ***Blender***, while still allowing to effortlessly share their work with non technical artists through ***Python*** and ***GLSL*** plugins. 14 | 15 | --- 16 | 17 |
18 | 21 |
22 | @pragma37 23 |
24 |
25 | 26 | --- 27 | 28 | - Free and **Open Source**. *MIT License* 29 | - **Real Time Rendering** 30 | - Full **Blender** integration 31 | - Built-in Pipeline for **Stylized Non Photorealistic Rendering** 32 | - Stylized shading models 33 | - Light Groups 34 | - Line Rendering 35 | - Fully customizable through nodes *(Materials, Light shaders, Screen shaders and even the render pipeline itself)* 36 | - Code as a *First Class Citizen* 37 | - *Auto-reloading* for everything 38 | - **VSCode** (including **GLSL** *intellisense*) and **Renderdoc** integration 39 | - Automatic generation of nodes from plain GLSL functions 40 | - Automatic UI for Shader and Pipeline parameters 41 | - 100% customizable Python Render Pipelines 42 | 43 | --- 44 | 45 |
46 | 49 |
50 | @pragma37 51 |
52 |
53 | 54 | --- 55 | 56 |
57 | 60 |
61 | @Renato3xl 62 |
63 |
64 | -------------------------------------------------------------------------------- /docs/overrides/partials/_footer.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/docs/overrides/partials/_footer.html -------------------------------------------------------------------------------- /docs/reference/Render Layer-graph.md: -------------------------------------------------------------------------------- 1 | # Render Layer Graph Reference 2 | --- 3 | ## Render 4 | --- 5 | ### **LineRender** 6 | Expands the line up to the width especified in the *Line Width* texture 7 | and composites it on top of the *Color* texture. 8 | 9 | - **Inputs** 10 | - **Color** *: ( Texture )* 11 | - **Line Color** *: ( Texture )* 12 | - **Line Width** *: ( Texture )* 13 | - **Max Width** *: ( Int ) - default = 10* 14 | >The maximum width the shader can render. 15 | Increasing the value lowers the render performance. 16 | - **Line Scale** *: ( Float ) - default = 1.0* 17 | >Scale all Line Width values with this one. 18 | *(Useful for rendering at different resolutions)* 19 | - **Normal Depth** *: ( Texture )* 20 | - **ID** *: ( Texture )* 21 | - **Outputs** 22 | - **Color** *: ( Texture )* 23 | --- 24 | ### **SuperSamplingAA** 25 | Performs anti-aliasing by accumulating multiple render samples into a single texture. 26 | 27 | - **Inputs** 28 | - **Color** *: ( Texture )* 29 | - **Outputs** 30 | - **Color** *: ( Texture )* 31 | --- 32 | ### **MainPass** 33 | >Graph Type / Pass : *Mesh / MAIN_PASS_PIXEL_SHADER* 34 | 35 | Renders the scene geometry using the *Mesh Main Pass*. 36 | The node sockets are dynamic, based on the *Main Pass Custom IO*. 37 | If *Normal Depth/ID* is empty, the *Pre Pass* *Normal Depth/ID* will be used. 38 | 39 | - **Inputs** 40 | - **Scene** *: ( Scene )* 41 | - **Normal Depth** *: ( Texture )* 42 | - **ID** *: ( Texture )* 43 | --- 44 | ### **PrePass** 45 | >Graph Type / Pass : *Mesh / PRE_PASS_PIXEL_SHADER* 46 | 47 | Renders the scene geometry using the *Mesh Pre Pass*. 48 | The node sockets are dynamic, based on the *Pre Pass Custom IO*. 49 | If *Normal Depth/ID* is empty, the *PrePass* *Normal Depth/ID* will be used. 50 | 51 | - **Inputs** 52 | - **Scene** *: ( Scene )* 53 | - **Outputs** 54 | - **Scene** *: ( Scene )* 55 | - **Normal Depth** *: ( Texture )* 56 | - **ID** *: ( Texture )* 57 | --- 58 | ### **ScreenPass** 59 | >Graph Type / Pass : *Screen / SCREEN_SHADER* 60 | 61 | Renders a full screen shader pass. 62 | The node sockets are dynamic, based on the shader selected. 63 | If *Normal Depth/ID* is empty, the *PrePass* *Normal Depth/ID* will be used. 64 | 65 | - **Inputs** 66 | - **Layer Only** *: ( Bool ) - default = True* 67 | >Draw only on top of the current layer geometry, 68 | to avoid accidentally covering previous layers. 69 | - **Scene** *: ( Scene )* 70 | - **Normal Depth** *: ( Texture )* 71 | - **ID** *: ( Texture )* 72 | -------------------------------------------------------------------------------- /docs/reference/Render-graph.md: -------------------------------------------------------------------------------- 1 | # Render Graph Reference 2 | --- 3 | ## Render 4 | --- 5 | ### **LineRender** 6 | Expands the line up to the width especified in the *Line Width* texture 7 | and composites it on top of the *Color* texture. 8 | 9 | - **Inputs** 10 | - **Color** *: ( Texture )* 11 | - **Line Color** *: ( Texture )* 12 | - **Line Width** *: ( Texture )* 13 | - **Max Width** *: ( Int ) - default = 10* 14 | >The maximum width the shader can render. 15 | Increasing the value lowers the render performance. 16 | - **Line Scale** *: ( Float ) - default = 1.0* 17 | >Scale all Line Width values with this one. 18 | *(Useful for rendering at different resolutions)* 19 | - **Normal Depth** *: ( Texture )* 20 | - **ID** *: ( Texture )* 21 | - **Outputs** 22 | - **Color** *: ( Texture )* 23 | --- 24 | ### **SuperSamplingAA** 25 | Performs anti-aliasing by accumulating multiple render samples into a single texture. 26 | 27 | - **Inputs** 28 | - **Color** *: ( Texture )* 29 | - **Outputs** 30 | - **Color** *: ( Texture )* 31 | --- 32 | ### **RenderLayers** 33 | >Graph Type / Pass : *Render Layer / Render Layer* 34 | 35 | Renders the scene geometry, using multiple *depth peeling* layers for transparent objects. 36 | The node sockets are dynamic, based on the graph selected. 37 | 38 | - **Inputs** 39 | - **Scene** *: ( Scene )* 40 | - **Transparent Layers** *: ( Int ) - default = 4* 41 | >The maximum number of overlapping transparency layers. 42 | Incresing this values lowers performance. 43 | --- 44 | ### **SceneLighting** 45 | Renders the shadow maps and attaches them along the scene lights data to the *Scene* shader resources. 46 | 47 | - **Inputs** 48 | - **Scene** *: ( Scene )* 49 | - **Point Resolution** *: ( Int ) - default = 2048* 50 | >Shadowmap resolution for point lights *(for each cubemap side)*. 51 | - **Spot Resolution** *: ( Int ) - default = 2048* 52 | >Shadowmap resolution for spot lights. 53 | - **Sun Resolution** *: ( Int ) - default = 2048* 54 | >Shadowmap resolution for sun light lights *(for each cascade side)*. 55 | - **Sun Max Distance** *: ( Float ) - default = 100* 56 | >The maximum distance from the view origin at which objects will still cast shadows. 57 | The lower the value, the higher the perceived resolution. 58 | - **Sun CSM Count** *: ( Int ) - default = 4* 59 | >The number of [Shadow Cascades](https://docs.microsoft.com/en-us/windows/win32/dxtecharts/cascaded-shadow-maps#cascaded-shadow-maps-and-perspective-aliasing) for sun lights. 60 | - **Sun CSM Distribution** *: ( Float ) - default = 0.9* 61 | >Interpolates the cascades distribution along the view distance between linear distribution *(at 0)* and logarithmic distribution *(at 1)*. 62 | The appropriate value depends on camera FOV and scene characteristics. 63 | - **Outputs** 64 | - **Scene** *: ( Scene )* 65 | --- 66 | ### **ScreenPass** 67 | >Graph Type / Pass : *Screen / SCREEN_SHADER* 68 | 69 | Renders a full screen shader pass. 70 | The node sockets are dynamic, based on the shader selected. 71 | 72 | -------------------------------------------------------------------------------- /docs/reference/settings.md: -------------------------------------------------------------------------------- 1 | # Malt Settings 2 | ## World 3 | - **Material** 4 | - **Default** *: ( Material )** = Malt - Default Mesh Material* 5 | >The default material, used for objects with no material assigned. 6 | - **Override** *: ( Material )** = None* 7 | >When set, overrides all scene materials with this one. 8 | - **Viewport** 9 | - **Resolution Scale** *: ( Float )** = 1.0* 10 | >A multiplier for the viewport resolution. 11 | It can be lowered to improve viewport performance or for specific styles, like *pixel art*. 12 | - **Smooth Interpolation** *: ( Bool )** = True* 13 | >The interpolation mode used when *Resolution Scale* is not 1. 14 | Toggles between *Nearest/Bilinear* interpolation. 15 | - **Samples** 16 | - **Grid Size** *: ( Int )** = 8* 17 | >The number of render samples per side in the sampling grid. 18 | The total number of samples is the square of this value minus the samples that fall outside the sampling radius. 19 | Higher values will provide cleaner renders at the cost of increased render times. 20 | - **Width** *: ( Float )** = 1.0* 21 | >The width (and height) of the sampling grid. 22 | Larger values will result in smoother/blurrier images while lower values will result in sharper/more aliased ones. 23 | Keep it withing the 1-2 range for best results. 24 | - **Render** *: ( Graph )** = Default Render* 25 | >The *Render Node Tree* used to render the scene. 26 | See [Render & Render Layers](#Render & Render Layers) for more info. 27 | ## Material 28 | - **Light Groups** 29 | - **Light** *: ( Int )** = [1, 0, 0, 0]* 30 | >The *Light Groups* (up to 4) that lit this material. 31 | - **Shadow** *: ( Int )** = [1, 0, 0, 0]* 32 | >The *Light Groups* (up to 4) that this material casts shadows on. 33 | ## Mesh 34 | - **double_sided** *: ( Bool )** = False* 35 | >Disables backface culling, so geometry is rendered from both sides. 36 | - **precomputed_tangents** *: ( Bool )** = False* 37 | >Load precomputed mesh tangents *(needed for improving normal mapping quality on low poly meshes)*. 38 | It's disabled by default since it slows down mesh loading in Blender. 39 | When disabled, the *tangents* are calculated on the fly from the *pixel shader*. 40 | ## Light 41 | - **Light Group** *: ( Int )** = 1* 42 | >Lights only affect materials with a matching *Light Group* value. 43 | - **Shader** *: ( Material )** = None* 44 | >When set, the *Material* with a custom *Light Shader* or *Light Node Tree* that will be used to render this light. 45 | -------------------------------------------------------------------------------- /mkdocs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Malt 2 | 3 | site_url: https://malt3d.com 4 | site_author: Miguel Pozo 5 | site_description: Malt Render Engine 6 | repo_name: bnpr/Malt 7 | repo_url: https://github.com/bnpr/Malt 8 | docs_dir: ../docs/ 9 | #edit_uri: "" 10 | 11 | nav: 12 | - 'index.md' 13 | - 'Documentation': 14 | - 'Documentation/Getting Started.md' 15 | - 'Documentation/Settings.md' 16 | - 'Documentation/Graphs.md' 17 | - 'Reference': 18 | - 'reference/settings.md' 19 | - 'reference/Mesh-graph.md' 20 | - 'reference/Screen-graph.md' 21 | - 'reference/Light-graph.md' 22 | - 'reference/Render-graph.md' 23 | - 'reference/Render Layer-graph.md' 24 | - 'Documentation/Plugins.md' 25 | - 'Documentation/Tooling.md' 26 | #- 'Developer Documentation' : 'https://github.com/bnpr/Malt/tree/Development/Malt#malt' 27 | - 'Discussions - Q&A' : 'https://github.com/bnpr/Malt/discussions' 28 | - 'Patreon' : 'https://patreon.com/pragma37' 29 | - 'Download' : 'https://github.com/bnpr/Malt/releases/tag/Release-latest' 30 | 31 | theme: 32 | name: material 33 | custom_dir: ../docs/overrides 34 | logo: images/cube-solid.svg 35 | favicon: images/cube-solid.svg 36 | features: 37 | - navigation.instant 38 | #- navigation.tracking 39 | - navigation.tabs 40 | #- toc.integrate 41 | palette: 42 | - scheme: slate 43 | primary: teal 44 | toggle: 45 | icon: material/toggle-switch-off-outline 46 | name: Switch to light mode 47 | - scheme: default 48 | primary: teal 49 | toggle: 50 | icon: material/toggle-switch 51 | name: Switch to dark mode 52 | 53 | extra_css: 54 | - extra/extra.css 55 | 56 | extra_javascript: 57 | - extra/extra.js 58 | 59 | markdown_extensions: 60 | - attr_list 61 | - pymdownx.highlight: 62 | use_pygments: true 63 | anchor_linenums: true 64 | - pymdownx.inlinehilite 65 | - pymdownx.snippets 66 | - pymdownx.superfences 67 | - pymdownx.superfences: 68 | custom_fences: 69 | - name: mermaid 70 | class: mermaid 71 | format: !!python/name:pymdownx.superfences.fence_code_format 72 | 73 | extra: 74 | generator: false 75 | social: 76 | - icon: fontawesome/brands/github 77 | link: https://github.com/bnpr/Malt 78 | - icon: fontawesome/brands/youtube 79 | link: https://www.youtube.com/channel/UCNjoz44PbgbQPqFrqy7Y4mg 80 | - icon: fontawesome/brands/twitter 81 | link: https://twitter.com/pragma37 82 | - icon: fontawesome/brands/patreon 83 | link: https://www.patreon.com/pragma37 84 | 85 | -------------------------------------------------------------------------------- /mkdocs/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "site/" 3 | command = "mkdocs build" 4 | ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../docs/" 5 | -------------------------------------------------------------------------------- /mkdocs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material==8.2.8 2 | -------------------------------------------------------------------------------- /plugins/Experimental/RenderLayer/OpaqueLayer.py: -------------------------------------------------------------------------------- 1 | from Malt.PipelineNode import PipelineNode 2 | from Malt.PipelineParameters import Parameter, Type 3 | 4 | class OpaqueLayer(PipelineNode): 5 | 6 | def __init__(self, pipeline): 7 | PipelineNode.__init__(self, pipeline) 8 | 9 | @classmethod 10 | def reflect_inputs(cls): 11 | return { 12 | 'Output Name' : Parameter('', Type.STRING) 13 | } 14 | 15 | @classmethod 16 | def reflect_outputs(cls): 17 | return { 18 | 'Opaque Layer' : Parameter('', Type.TEXTURE) 19 | } 20 | 21 | def execute(self, parameters): 22 | inputs = parameters['IN'] 23 | outputs = parameters['OUT'] 24 | 25 | output_name = inputs['Output Name'] 26 | render_layers_node = parameters['__GLOBALS__']['__RENDER_LAYERS__'] 27 | outputs['Opaque Layer'] = render_layers_node.opaque_targets.get(output_name) 28 | 29 | NODE = OpaqueLayer 30 | -------------------------------------------------------------------------------- /plugins/Experimental/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Malt.PipelinePlugin import PipelinePlugin, isinstance_str 3 | 4 | class ExperimentalNodes(PipelinePlugin): 5 | 6 | @classmethod 7 | def poll_pipeline(self, pipeline): 8 | return isinstance_str(pipeline, 'NPR_Pipeline') 9 | 10 | @classmethod 11 | def register_graph_libraries(self, graphs): 12 | root = os.path.dirname(__file__) 13 | graphs['Render Layer'].add_library(os.path.join(root, 'RenderLayer')) 14 | 15 | PLUGIN = ExperimentalNodes 16 | -------------------------------------------------------------------------------- /plugins/PluginExample/Shaders/PluginExample.glsl: -------------------------------------------------------------------------------- 1 | #include "Common.glsl" 2 | #include "Common/Hash.glsl" 3 | 4 | vec4 instance_random_color() 5 | { 6 | return hash(ID[0]); 7 | } 8 | -------------------------------------------------------------------------------- /plugins/PluginExample/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Malt.PipelinePlugin import PipelinePlugin, isinstance_str 3 | 4 | class PluginExample(PipelinePlugin): 5 | 6 | @classmethod 7 | def poll_pipeline(self, pipeline): 8 | return isinstance_str(pipeline, 'NPR_Pipeline') 9 | 10 | @classmethod 11 | def register_graph_libraries(self, graphs): 12 | library_path = os.path.join(os.path.dirname(__file__), 'Shaders') 13 | for graph in graphs.values(): 14 | if graph.language == 'GLSL': 15 | graph.add_library(library_path) 16 | 17 | PLUGIN = PluginExample 18 | -------------------------------------------------------------------------------- /scripts/PatchDependencies/mcpp-Darwin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/mcpp-Darwin -------------------------------------------------------------------------------- /scripts/PatchDependencies/mcpp-Linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/mcpp-Linux -------------------------------------------------------------------------------- /scripts/PatchDependencies/mcpp-Windows.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/mcpp-Windows.exe -------------------------------------------------------------------------------- /scripts/PatchDependencies/python-gpu-310.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/python-gpu-310.exe -------------------------------------------------------------------------------- /scripts/PatchDependencies/python-gpu-311.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/python-gpu-311.exe -------------------------------------------------------------------------------- /scripts/PatchDependencies/python-gpu-37.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/python-gpu-37.exe -------------------------------------------------------------------------------- /scripts/PatchDependencies/python-gpu-39.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnpr/Malt/725f509ab25be736cb592cf1e9d5258ed4271e8a/scripts/PatchDependencies/python-gpu-39.exe -------------------------------------------------------------------------------- /scripts/PatchDependencies/python-gpu.c: -------------------------------------------------------------------------------- 1 | /* Minimal main program -- everything is loaded from the library */ 2 | 3 | #include "Python.h" 4 | 5 | #ifdef MS_WINDOWS 6 | typedef unsigned long DWORD; 7 | _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; 8 | _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0x00000001; 9 | int 10 | wmain(int argc, wchar_t **argv) 11 | { 12 | printf("Python GPU\n"); 13 | return Py_Main(argc, argv); 14 | } 15 | #else 16 | int 17 | main(int argc, char **argv) 18 | { 19 | return Py_BytesMain(argc, argv); 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /scripts/format.py: -------------------------------------------------------------------------------- 1 | def scan_dirs(path, file_callback): 2 | import os 3 | for e in os.scandir(path): 4 | if e.is_file(): 5 | extension = e.name.split('.')[-1] 6 | if extension in ('py','glsl','h','c','cpp'): 7 | file_callback(e) 8 | if e.is_dir(): 9 | if e.name.startswith('.') or e.name.startswith('__'): 10 | continue 11 | scan_dirs(e, file_callback) 12 | 13 | 14 | def fix_whitespace(path): 15 | with open(path, 'r+') as f: 16 | result = '' 17 | lines = f.readlines() 18 | for i, line in enumerate(lines): 19 | new_line = '' 20 | if line.isspace(): 21 | while i < len(lines) - 1: 22 | i += 1 23 | found_next_line = False 24 | if lines[i].isspace() == False: 25 | for c in lines[i]: 26 | if c.isspace(): 27 | new_line += c 28 | else: 29 | break 30 | found_next_line = True 31 | else: 32 | continue 33 | if found_next_line: 34 | break 35 | else: 36 | new_line = line.rstrip() 37 | result += new_line 38 | result += '\n' 39 | result = result.splitlines(keepends=True) 40 | while len(result) > 1 and result[-1].isspace(): 41 | while result[-1].isspace(): 42 | result.pop() 43 | result = ''.join(result) 44 | f.seek(0) 45 | f.truncate() 46 | f.write(result) 47 | 48 | 49 | import sys 50 | 51 | scan_dirs(sys.argv[1], fix_whitespace) 52 | -------------------------------------------------------------------------------- /scripts/generate_conversion_nodes.py: -------------------------------------------------------------------------------- 1 | result = '' 2 | 3 | base_types = ('float','int','uint','bool') 4 | vector_types = ('vec','ivec','uvec','bvec') 5 | 6 | for to_base_type in base_types: 7 | result += f'//{to_base_type}\n' 8 | for from_base_type in base_types: 9 | if from_base_type == to_base_type: 10 | continue 11 | result += '/* META @meta: internal=true; */\n' 12 | param = from_base_type[:1] 13 | result += f'{to_base_type} {to_base_type}_from_{from_base_type}({from_base_type} {param}) {{ return {to_base_type}({param}); }}\n' 14 | 15 | result += '\n' 16 | 17 | for to_vector_type in vector_types: 18 | for to_vector_len in range(2,5): 19 | to_vector = f'{to_vector_type}{to_vector_len}' 20 | 21 | result += f'//{to_vector}\n' 22 | 23 | for base_type in base_types: 24 | result += '/* META @meta: internal=true; */\n' 25 | param = base_type[:1] 26 | conversion = f'{to_vector}({param})' 27 | if to_vector_len == 4 and to_vector_type != 'bvec': 28 | conversion = f'{to_vector}({param}, {param}, {param}, 1)' 29 | result += f'{to_vector} {to_vector}_from_{base_type}({base_type} {param}) {{ return {conversion}; }}\n' 30 | 31 | for from_vector_type in vector_types: 32 | for from_vector_len in range(2,5): 33 | if from_vector_type == to_vector_type and from_vector_len == to_vector_len: 34 | continue 35 | from_vector = f'{from_vector_type}{from_vector_len}' 36 | 37 | param = 'v' 38 | if to_vector_len < from_vector_len: 39 | param += '.xyzw'[:to_vector_len+1] 40 | if to_vector_len > from_vector_len: 41 | for i in range(from_vector_len, to_vector_len): 42 | if to_vector_type != 'bvec' and to_vector_len == 4 and i == to_vector_len - 1: 43 | param += ', 1' 44 | else: 45 | param += ', 0' 46 | conversion = f'{to_vector}({param})' 47 | 48 | result += '/* META @meta: internal=true; */\n' 49 | result += f'{to_vector} {to_vector}_from_{from_vector}({from_vector} v) {{ return {conversion}; }}\n' 50 | 51 | result += '\n' 52 | 53 | 54 | print(result) 55 | -------------------------------------------------------------------------------- /scripts/generate_license_dependencies_full.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | TOP_DIR = os.path.join(os.path.dirname(__file__), os.pardir) 4 | 5 | dependencies = open(os.path.join(TOP_DIR, 'LICENSE - DEPENDENCIES')).read() 6 | 7 | result = '' 8 | 9 | for dependency in dependencies.split('\n\n'): 10 | lines = dependency.splitlines() 11 | for i, line in enumerate(lines): 12 | if i < 3: 13 | result += line 14 | else: 15 | url = line.replace('github.com', 'raw.githubusercontent.com').replace('blob/','') 16 | from urllib.request import urlopen 17 | content = urlopen(url).read().decode('utf-8') 18 | result+=content 19 | result+='\n' 20 | result += '*'*80 21 | result+='\n' 22 | 23 | open(os.path.join(TOP_DIR, 'LICENSE - DEPENDENCIES (FULL TEXT)'), 'w').write(result) 24 | -------------------------------------------------------------------------------- /scripts/get_glslang.py: -------------------------------------------------------------------------------- 1 | import os, stat, platform, shutil, zipfile 2 | 3 | current_dir = os.path.dirname(os.path.realpath(__file__)) 4 | gl_folder = os.path.join(current_dir, '..', 'Malt', 'GL') 5 | 6 | zip_file = os.path.join(current_dir, 'glslang.zip') 7 | 8 | glslang_url = { 9 | 'Windows' : "https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip", 10 | 'Linux' : "https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip", 11 | 'Darwin' : "https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-osx-Release.zip", 12 | } 13 | glslang_url = glslang_url[platform.system()] 14 | 15 | zipped_path = 'bin/glslangValidator' 16 | target_path = os.path.join(gl_folder, '.glslang') 17 | if platform.system() == 'Windows': 18 | zipped_path += '.exe' 19 | target_path += '.exe' 20 | 21 | import urllib.request 22 | urllib.request.urlretrieve(glslang_url, zip_file) 23 | 24 | with zipfile.ZipFile(zip_file) as z: 25 | with z.open(zipped_path) as zip, open(target_path, 'wb') as unzip: 26 | shutil.copyfileobj(zip, unzip) 27 | 28 | # Set as executable (Needed on Linux) 29 | os.chmod(target_path, os.stat(target_path).st_mode | stat.S_IEXEC) 30 | 31 | os.remove(zip_file) 32 | -------------------------------------------------------------------------------- /scripts/install_dependencies.py: -------------------------------------------------------------------------------- 1 | import os, subprocess, sys, shutil, stat 2 | 3 | current_dir = os.path.dirname(os.path.realpath(__file__)) 4 | 5 | malt_folder = os.path.join(current_dir, '..', 'Malt') 6 | 7 | try: 8 | subprocess.check_call([sys.executable, '-m', 'pip', '--version']) 9 | except: 10 | subprocess.check_call([sys.executable, '-m', 'ensurepip']) 11 | os.environ.pop("PIP_REQ_TRACKER", None) 12 | 13 | py_version = str(sys.version_info[0])+str(sys.version_info[1]) 14 | malt_dependencies_path = os.path.join(malt_folder, '.Dependencies-{}'.format(py_version)) 15 | dependencies = ['glfw', 'PyOpenGL', 'PyOpenGL_accelerate', 'Pyrr', 'xxhash'] 16 | for dependency in dependencies: 17 | try: 18 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--upgrade', dependency, '--target', malt_dependencies_path]) 19 | except: 20 | print('ERROR: pip install {} failed.'.format(dependency)) 21 | import traceback 22 | traceback.print_exc() 23 | 24 | 25 | from distutils.dir_util import copy_tree 26 | copy_tree(os.path.join(current_dir, 'PatchDependencies'), malt_dependencies_path) 27 | 28 | #make sure mcpp has executable permissions 29 | for str in ['Linux', 'Darwin']: 30 | mcpp = os.path.join(malt_dependencies_path, f'mcpp-{str}') 31 | os.chmod(mcpp, os.stat(mcpp).st_mode | stat.S_IEXEC) 32 | 33 | #Remove numpy since Blender already ships with it 34 | #Remove bin to avoid AVs false positives 35 | for e in os.listdir(malt_dependencies_path): 36 | if e.startswith('numpy') or e == 'bin': 37 | path = os.path.join(malt_dependencies_path, e) 38 | if os.path.isdir(path): 39 | shutil.rmtree(path) 40 | elif os.path.isfile(path): 41 | os.remove(path) 42 | 43 | 44 | #subprocess.check_call([sys.executable, os.path.join(current_dir, 'get_glslang.py')]) 45 | -------------------------------------------------------------------------------- /scripts/print_pixel_formats.py: -------------------------------------------------------------------------------- 1 | GL_ENUMS = {} 2 | GL_NAMES = {} 3 | 4 | if True: #create new scope to import OpenGL 5 | from OpenGL import GL 6 | for e in dir(GL): 7 | if e.startswith('GL_'): 8 | GL_ENUMS[getattr(GL, e)] = e 9 | GL_NAMES[e] = getattr(GL, e) 10 | 11 | from OpenGL.GL import * 12 | 13 | def print_format_prop(format, prop): 14 | read = glGetInternalformativ(GL_TEXTURE_2D, format, prop, 1) 15 | print(GL_ENUMS[format], GL_ENUMS[prop], GL_ENUMS[read]) 16 | 17 | def print_format_props(format): 18 | print_format_prop(format, GL_READ_PIXELS) 19 | print_format_prop(format, GL_READ_PIXELS_FORMAT) 20 | print_format_prop(format, GL_READ_PIXELS_TYPE) 21 | print_format_prop(format, GL_TEXTURE_IMAGE_FORMAT) 22 | print_format_prop(format, GL_TEXTURE_IMAGE_TYPE) 23 | 24 | print_format_props(GL_RGB8) 25 | print_format_props(GL_RGBA8) 26 | print_format_props(GL_RGB16F) 27 | print_format_props(GL_RGBA16F) 28 | print_format_props(GL_RGB32F) 29 | print_format_props(GL_RGBA32F) 30 | -------------------------------------------------------------------------------- /scripts/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.pyx": "python", 4 | "*.glsl": "cpp" 5 | }, 6 | "cmake.configureOnOpen": false, 7 | "C_Cpp.default.includePath": ["Malt\\Shaders"], 8 | "C_Cpp.autoAddFileAssociations": true, 9 | "C_Cpp.default.cppStandard": "c++03", 10 | "C_Cpp.default.compilerPath": "", 11 | "C_Cpp.default.browse.limitSymbolsToIncludedHeaders": true, 12 | "C_Cpp.errorSquiggles": "Disabled", 13 | } -------------------------------------------------------------------------------- /scripts/setup_blender_addon.py: -------------------------------------------------------------------------------- 1 | import os, sys, platform, subprocess, shutil, pathlib 2 | 3 | import argparse 4 | parser = argparse.ArgumentParser() 5 | parser.add_argument('--scripts-folder', type=pathlib.Path, help='Create a symlink pointing to the addon.') 6 | parser.add_argument('--copy-modules', action='store_true', help='Copy Malt and Bridge instead of making a symlink. (Needed for zipping in Linux)') 7 | ARGS = parser.parse_args() 8 | 9 | current_dir = os.path.dirname(os.path.realpath(__file__)) 10 | main_dir = os.path.realpath(os.path.join(current_dir, '..')) 11 | 12 | blender_malt_folder = os.path.join(main_dir, 'BlenderMalt') 13 | bridge_folder = os.path.join(main_dir, 'Bridge') 14 | malt_folder = os.path.join(main_dir, 'Malt') 15 | 16 | def build_lib(path): 17 | subprocess.check_call([sys.executable, 'build.py'], cwd=path) 18 | import stat 19 | def delete_read_only(func, path, exc_info): 20 | os.chmod(path, stat.S_IWRITE) 21 | os.remove(path) 22 | shutil.rmtree(os.path.join(path, '.build'), onerror=delete_read_only) 23 | 24 | build_lib(os.path.join(blender_malt_folder, 'CBlenderMalt')) 25 | build_lib(os.path.join(malt_folder, 'GL', 'GLSLParser')) 26 | build_lib(os.path.join(bridge_folder, 'ipc')) 27 | build_lib(os.path.join(bridge_folder, 'renderdoc')) 28 | 29 | subprocess.check_call([sys.executable, os.path.join(current_dir, 'install_dependencies.py')]) 30 | 31 | def ensure_dir(path): 32 | if os.path.exists(path) == False: 33 | os.mkdir(path) 34 | 35 | def make_link(point_from, point_to): 36 | if os.path.exists(point_from): 37 | print('Already linked:', point_from, '<--->', point_to) 38 | return 39 | if platform.system() == 'Windows': 40 | import _winapi 41 | _winapi.CreateJunction(point_to, point_from) 42 | else: 43 | os.symlink(point_to, point_from, True) 44 | 45 | def make_copy(copy_to, copy_from): 46 | from distutils.dir_util import copy_tree 47 | copy_tree(copy_from, copy_to) 48 | 49 | import_path = os.path.join(blender_malt_folder, '.MaltPath') 50 | ensure_dir(import_path) 51 | 52 | setup_modules = make_link 53 | if ARGS.copy_modules: 54 | setup_modules = make_copy 55 | 56 | setup_modules(os.path.join(import_path, 'Malt'), os.path.join(main_dir, 'Malt')) 57 | setup_modules(os.path.join(import_path, 'Bridge'), os.path.join(main_dir, 'Bridge')) 58 | 59 | if ARGS.scripts_folder: 60 | addons_folder = os.path.join(ARGS.scripts_folder, 'addons') 61 | ensure_dir(addons_folder) 62 | make_link(os.path.join(addons_folder, 'BlenderMalt'), blender_malt_folder) 63 | --------------------------------------------------------------------------------