├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── create_addon_archive.py ├── docs ├── editor_screenshot.png ├── godot_asset_library │ ├── brain.jpg │ ├── icon.kra │ ├── icon.png │ └── iguana_skull.jpg ├── journal-mri_marching_cubes.txt ├── models │ ├── marching_cubes_test.gltf │ └── marching_cubes_test0.bin ├── script.txt ├── volume_layer_inspector_panel.png └── volume_layer_inspector_panel.png~ ├── godot ├── .gitattributes ├── .github │ └── FUNDING.yml ├── .gitignore ├── addons │ └── volume_layered_shader │ │ ├── art │ │ └── icons │ │ │ ├── volume_layered.svg │ │ │ └── volume_layered.svg.import │ │ ├── materials │ │ └── volume_layered_shader.tres │ │ ├── plugin.cfg │ │ ├── scenes │ │ └── controls │ │ │ └── volume_layered_shader.gd │ │ ├── scripts │ │ ├── glsl_shader_tools │ │ │ ├── glsl_shader_tool.gd │ │ │ ├── glsl_util.gd │ │ │ ├── mipmap_generator_rf_3d.gd │ │ │ ├── mipmap_generator_rgba8_3d.gd │ │ │ └── mipmap_generator_rgbaf_3d.gd │ │ ├── math │ │ │ └── image_gradient.gd │ │ └── resources │ │ │ ├── cpu │ │ │ ├── npy_image_cpu_rf_Texture3d.gd │ │ │ ├── zipped_image_archive_cpu_Texture3D.gd │ │ │ └── zipped_image_stack.gd │ │ │ └── gpu │ │ │ ├── npy_image_rf_Texture3D.gd │ │ │ ├── npy_loader.gd │ │ │ ├── zipped_image_archive_rf_3d.gd │ │ │ └── zipped_image_archive_rf_Texture3D.gd │ │ ├── shaders │ │ ├── mipmap_generator_rf_3d.glsl │ │ ├── mipmap_generator_rf_3d.glsl.import │ │ ├── mipmap_generator_rgba8_3d.glsl │ │ ├── mipmap_generator_rgba8_3d.glsl.import │ │ ├── mipmap_generator_rgbaf_3d.glsl │ │ ├── mipmap_generator_rgbaf_3d.glsl.import │ │ └── volume_shader.gdshader │ │ ├── textures │ │ ├── low_values_grtadient.tres │ │ ├── purple_gradient.tres │ │ └── purple_gradient_texture.tres │ │ └── volume_layered_shader_addon.gd ├── art │ ├── iguana │ │ └── Iguana_png.zip │ ├── iguana_skull.png │ ├── iguana_skull.png.import │ └── noise_data_4d.npy ├── data │ ├── iguana_zip_layers_cpu.tres │ ├── iguana_zip_layers_gpu.tres │ ├── npy_noise_4d_cpu.tres │ └── npy_noise_4d_gpu.tres ├── doc │ └── script.txt ├── icon.svg ├── icon.svg.import ├── marching_cubes_bloyd_viewer0.bin ├── marching_cubes_bloyd_viewer_bloyd0.bin ├── project.godot ├── scenes │ ├── controls │ │ ├── trackball_tumbler.gd │ │ └── trackball_tumbler.tscn │ ├── demos │ │ ├── noise_AnimationPlayer_demo.tscn │ │ ├── noise_animation_player_demo.gd │ │ └── noise_demo.tscn │ ├── main_volume_layered_shader.gd │ ├── main_volume_layered_shader.tscn │ └── player_buttons.tres └── testing │ └── create_noise_volume.py ├── sources.txt └── video └── models └── cube.blend /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: markmckay 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: kitfox_com 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /video/models/*.blend1 2 | /godot/cube_code.txt 3 | /export 4 | /godot/cube_code_glsl.txt 5 | /godot/cube_code_glsl_fixed.txt 6 | /godot/cube_code_glsl_var.txt 7 | /godot/scenes/tests/*.tmp 8 | /bugs 9 | /godot/dump_buffer.txt 10 | /downloads 11 | /Iguana.nii 12 | /Iguana_png 13 | /Iguana.nii.gz 14 | /build 15 | /godot/art/chris-MRAnii.zip 16 | /godot/art/chris-PDnii.zip 17 | /godot/art/CT-Abdonii.zip 18 | /godot/art/CT-AVMnii.zip 19 | /godot/art/CT-Electrodesnii.zip 20 | /godot/art/CT-Philipsnii.zip 21 | /godot/art/fmri-pitchnii.zip 22 | /godot/art/MR-Gdnii.zip 23 | /godot/art/spmMotornii.zip 24 | /godot/art/visiblehumannii.zip 25 | /docs/godot_asset_library/*.kra~ 26 | /docs/godot_asset_library/*.png~ 27 | /docs/godot_asset_library/*.jpg~ 28 | /testing 29 | /godot/from_client 30 | /experiments/math_dev/venv 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Volume Layers for Godot 2 | 3 | [![Video - overview](https://img.youtube.com/vi/1IvKWIieZPM/0.jpg)](https://www.youtube.com/watch?v=1IvKWIieZPM) 4 | [![Video - using the cutting planes](https://img.youtube.com/vi/FzYEshB9TFQ/0.jpg)](https://www.youtube.com/watch?v=FzYEshB9TFQ) 5 | 6 | This is an addon for Godot that lets you view volumetric data, such as MRI scans. 7 | 8 | This addon uses zipped files of 2D images as source data, so any 3D volumetric files such as.nii will need to be converted to this first. Online programs such as https://www.onlineconverter.com/nifti-to-png can be used to convert your data. 9 | 10 | ![Shader in editor viewport](docs/editor_screenshot.png) 11 | 12 | ## Installation 13 | 14 | Copy the /addons/volume_layered_shader into your project in a directory of the same name. 15 | 16 | Make sure the addon is enabled in the Project Settigns/Plugins window. 17 | 18 | ## Usage 19 | 20 | Create a new instance of the addon by clicking the + button in the Scene window and selecting VolumeLayeredShader. 21 | 22 | You can set the image volume used by clicking in the Texture field and then creating a new 3D image texture (such as a ZippedImageArchiveCpuTexture3D). This 3D texture will supply the data used in the visualization. 23 | 24 | The addon provides several options for Texture3D loaders: 25 | 26 | #### ZippedImageArchiveCpuTexture3D 27 | 28 | - Used to load images from a zip file of images of a common image format, such as PNG or JPEG. 29 | - Images are presumed to all be the same size and arranged in the correct slice order. 30 | - Create an instance of the ZippedImageArchiveCpuTexture3D and then set the Zip File field to the file path of the zip file with your images. 31 | 32 | #### ZippedImageArchiveGpuTexture3D 33 | 34 | - Similar to ZippedImageArchiveCpuTexture3D, but uses GPU acceleration to generate mipmaps for smoother visualization. 35 | - Used to load images from a zip file of images of a common image format, such as PNG or JPEG. 36 | - Images are presumed to all be the same size and arranged in the correct slice order. 37 | - Create an instance of the ZippedImageArchiveCpuTexture3D and then set the Zip File field to the file path of the zip file with your images. 38 | 39 | #### NpyImageTexture3D 40 | 41 | - Load data stored in the .npy file format. 42 | - This is the data format used by Python's Numpy to store data arrays to disk. 43 | - It is a fairly simple file format, which should make it easy to generate your own files, even if you aren't using Python. 44 | - The NpyImageTexture3D presumes that the .npy file contains a multidimensional array of numerical values. It was designed to handle 3D and 4D, but should work with lower dimensions too. 45 | - If 4D, the `frame` field of the NpyImageTexture3D resource can be set to indicate which 3D slice of the 4D array is to be shown. 46 | - [NPY website](https://numpy.org/devdocs/reference/generated/numpy.lib.format.html) 47 | 48 | 49 | ![Inspector shader](docs/volume_layer_inspector_panel.png) 50 | 51 | * Texture - A Texture3D that contains the volumetric data to view. 52 | * Num Layers - The number of slices to make parallel to the camera. The more slices, the higher the resolution. 53 | * Gamma - Adjusts the sharpness of the texture data. Smaller values bring out soft areas while larger values bring out hard areas. 54 | * Opacity - Multiplies the opacity of the final pixel. 55 | * Color Scalar - Multiplies the color of the gradient. Used to boost the strength of the gradient. 56 | * Gradient - Colors the pixels of the volume. Softer values will be colored with values on the left of the gradient and harder values will be colored with values from the right side of the gradient. It is recommended you make the left side of the gradient transparent so low density pixels are clear. 57 | * Exclusion Planes - Any node added to this array will act as a plane that cuts away part of the model to make it easier to see the interior. These nodes can be placed anywhere in your scene. Marker3D is the recommended object type, but any object that extends Node3D can act as a clipping plane. 58 | 59 | ## Acknowledgments 60 | 61 | Iguana MRI data taken from the niivue-images archive: 62 | 63 | https://github.com/neurolabusc/niivue-images/tree/main 64 | 65 | 66 | ## Support 67 | 68 | If you found this software useful, please consider buying me a coffee on Kofi. Every contribution helps me to make more software: 69 | 70 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Y8Y43J6OB) 71 | 72 | -------------------------------------------------------------------------------- /create_addon_archive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2023 Mark McKay 6 | # This file is for packaging Godot addons for various projects (https://github.com/blackears). 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | import os 27 | import shutil 28 | import sys 29 | import getopt 30 | import platform 31 | 32 | projectName = 'volume_layered_shader' 33 | version="_1_0_3" 34 | extensions = [".gd", ".tres", ".tscn", ".gdshader", ".gdshaderinc", ".glsl", ".cfg", ".txt", ".md", ".glb", ".gltf", ".jpg", ".jpeg", ".png", ".exr", ".bin", ".svg"] 35 | 36 | 37 | def copy_files_with_suffix(source_dir, dest_dir, suffixes): 38 | for root, dirs, files in os.walk(source_dir): 39 | for file in files: 40 | if any(file.endswith(suffix) for suffix in suffixes): 41 | source_path = os.path.join(root, file) 42 | dest_path = os.path.join(dest_dir, os.path.relpath(source_path, source_dir)) 43 | os.makedirs(os.path.dirname(dest_path), exist_ok=True) 44 | shutil.copy2(source_path, dest_path) 45 | 46 | def make(): 47 | 48 | #Delete any existing build directory 49 | if os.path.exists('build'): 50 | shutil.rmtree('build') 51 | 52 | copy_files_with_suffix("godot/addons/" + projectName + "/", "build/addons/" + projectName + "/", extensions); 53 | 54 | 55 | #Build addon zip file 56 | if not os.path.exists('export'): 57 | os.mkdir('export') 58 | 59 | shutil.make_archive("export/" + projectName + version, "zip", "build") 60 | 61 | 62 | 63 | if __name__ == '__main__': 64 | 65 | make() 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /docs/editor_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/editor_screenshot.png -------------------------------------------------------------------------------- /docs/godot_asset_library/brain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/godot_asset_library/brain.jpg -------------------------------------------------------------------------------- /docs/godot_asset_library/icon.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/godot_asset_library/icon.kra -------------------------------------------------------------------------------- /docs/godot_asset_library/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/godot_asset_library/icon.png -------------------------------------------------------------------------------- /docs/godot_asset_library/iguana_skull.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/godot_asset_library/iguana_skull.jpg -------------------------------------------------------------------------------- /docs/journal-mri_marching_cubes.txt: -------------------------------------------------------------------------------- 1 | creating CPU version of algorithm 2 | 3 | realizing that algorithm is wrong. Fixing by taking face color into consideration. 4 | 5 | -------------------------------------------------------------------------------- /docs/models/marching_cubes_test.gltf: -------------------------------------------------------------------------------- 1 | {"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":1110,"max":[0.990476,0.9375,0.98324],"min":[0.07619,0.0625,0.089385],"normalized":false,"type":"VEC3"},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":1110,"max":[1,1,1],"min":[-1,-1,-1],"normalized":false,"type":"VEC3"},{"bufferView":2,"byteOffset":0,"componentType":5125,"count":1110,"max":[1109],"min":[0],"normalized":false,"type":"SCALAR"},{"bufferView":3,"byteOffset":0,"componentType":5126,"count":504,"max":[1.066667,1,1.072626],"min":[0,0,0],"normalized":false,"type":"VEC3"},{"bufferView":4,"byteOffset":0,"componentType":5126,"count":504,"max":[1,1,1,1],"min":[0,0,0,1],"normalized":false,"type":"VEC4"}],"asset":{"generator":"Godot Engine v4.1.1.stable.official@bd6af8e0ea69167dd0627f3bd54f9105bda0f8b5","version":"2.0"},"bufferViews":[{"buffer":0,"byteLength":13320,"byteOffset":0},{"buffer":0,"byteLength":13320,"byteOffset":13320},{"buffer":0,"byteLength":4440,"byteOffset":26640},{"buffer":0,"byteLength":6048,"byteOffset":31080},{"buffer":0,"byteLength":8064,"byteOffset":37128}],"buffers":[{"byteLength":45192,"uri":"marching_cubes_test0.bin"}],"cameras":[{"perspective":{"yfov":1.30899691581726,"zfar":4000,"znear":0.0500000007450581},"type":"perspective"}],"extensions":{"KHR_lights_punctual":{"lights":[{"color":[1,1,1],"intensity":1,"range":340282346638528860000000000000000000000,"type":"directional"}]}},"extensionsUsed":["KHR_lights_punctual","KHR_materials_unlit"],"materials":[{"extensions":{},"pbrMetallicRoughness":{"baseColorFactor":[0,0,0.99999988079071,1],"metallicFactor":0,"roughnessFactor":1}},{"extensions":{"KHR_materials_unlit":{}},"pbrMetallicRoughness":{"baseColorFactor":[0.99999988079071,0.99999988079071,0.99999988079071,1],"metallicFactor":0,"roughnessFactor":1}}],"meshes":[{"extras":{"targetNames":[]},"primitives":[{"attributes":{"NORMAL":1,"POSITION":0},"indices":2,"material":0,"mode":4}]},{"extras":{"targetNames":[]},"primitives":[{"attributes":{"COLOR_0":4,"POSITION":3},"material":1,"mode":0}]}],"nodes":[{"children":[1,2,3,4],"extensions":{},"name":"marching_cubes_test"},{"extensions":{},"name":"WorldEnvironment"},{"camera":0,"extensions":{},"name":"Camera3D","translation":[0,0,3.04327011108398]},{"extensions":{"KHR_lights_punctual":{"light":0}},"name":"DirectionalLight3D","rotation":[-0.59658420085907,-0.19381332397461,0.240629151463509,0.740689754486084],"translation":[-4.43772983551025,1.72817003726959,0]},{"children":[5,6],"extensions":{},"name":"marching_cubes","translation":[-0.30660501122475,0.315620988607407,0.179627999663353]},{"extensions":{},"mesh":0,"name":"mesh"},{"extensions":{},"mesh":1,"name":"mesh_points"}],"scene":0,"scenes":[{"nodes":[0]}]} -------------------------------------------------------------------------------- /docs/models/marching_cubes_test0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/models/marching_cubes_test0.bin -------------------------------------------------------------------------------- /docs/script.txt: -------------------------------------------------------------------------------- 1 | Hexadecimal and binary 2 | 3 | Symmetries 4 | 5 | 6 | Credits: 7 | 8 | Iguana test data 9 | https://github.com/neurolabusc/niivue-images/blob/main/Iguana.nii.gz 10 | 11 | Online converter 12 | https://www.onlineconverter.com/nifti-to-png 13 | 14 | Carleton discussion 15 | https://www.cs.carleton.edu/cs_comps/0405/shape/marching_cubes.html 16 | 17 | Paul Bourke marching cubes discussion 18 | https://paulbourke.net/geometry/polygonise/ 19 | -------------------------------------------------------------------------------- /docs/volume_layer_inspector_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/volume_layer_inspector_panel.png -------------------------------------------------------------------------------- /docs/volume_layer_inspector_panel.png~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/docs/volume_layer_inspector_panel.png~ -------------------------------------------------------------------------------- /godot/.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /godot/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: blackears 4 | patreon: markmckay 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: kitfox_com 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /godot/.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/art/icons/volume_layered.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 41 | 48 | 55 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/art/icons/volume_layered.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://df1pchfaikhpo" 6 | path="res://.godot/imported/volume_layered.svg-4032300effd2e0d9f82979c9903ec826.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/volume_layered_shader/art/icons/volume_layered.svg" 14 | dest_files=["res://.godot/imported/volume_layered.svg-4032300effd2e0d9f82979c9903ec826.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/materials/volume_layered_shader.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ShaderMaterial" load_steps=4 format=3 uid="uid://b474tuur3w8tn"] 2 | 3 | [ext_resource type="Shader" path="res://addons/volume_layered_shader/shaders/volume_shader.gdshader" id="1_6cohb"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_4ehgp"] 6 | offsets = PackedFloat32Array(0, 0.0410256, 1) 7 | colors = PackedColorArray(0.541176, 0.160784, 0.85098, 0, 0.541176, 0.160784, 0.85098, 0, 1, 1, 1, 1) 8 | 9 | [sub_resource type="GradientTexture1D" id="GradientTexture1D_yr4w7"] 10 | gradient = SubResource("Gradient_4ehgp") 11 | 12 | [resource] 13 | resource_local_to_scene = true 14 | render_priority = 0 15 | shader = ExtResource("1_6cohb") 16 | shader_parameter/zoom = 2.0 17 | shader_parameter/layers = 100 18 | shader_parameter/opacity = 1.0 19 | shader_parameter/gamma = 1.0 20 | shader_parameter/color_scalar = 1.0 21 | shader_parameter/num_exclusion_planes = 0 22 | shader_parameter/exclusion_planes = null 23 | shader_parameter/gradient = SubResource("GradientTexture1D_yr4w7") 24 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="VolumeLayeredShader" 4 | description="Render 3D volumes in the viewport using translucent layers." 5 | author="Mark McKay" 6 | version="1.1.0" 7 | script="volume_layered_shader_addon.gd" 8 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scenes/controls/volume_layered_shader.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends Node3D 26 | class_name VolumeLayers 27 | 28 | @export var texture:Texture3D: 29 | get: 30 | return texture 31 | set(value): 32 | if texture == value: 33 | return 34 | 35 | if texture: 36 | texture.changed.disconnect(on_texture_changed) 37 | 38 | texture = value 39 | 40 | if texture: 41 | texture.changed.connect(on_texture_changed) 42 | 43 | 44 | 45 | @export var num_layers:int = 10: 46 | get: 47 | return num_layers 48 | set(value): 49 | if value == num_layers: 50 | return 51 | num_layers = value 52 | rebuild_layers = true 53 | 54 | @export_range(0, 4, .01, "or_greater") var gamma:float = 1: 55 | get: 56 | return gamma 57 | set(value): 58 | if value == gamma: 59 | return 60 | gamma = value 61 | rebuild_layers = true 62 | 63 | @export_range(0, 1) var opacity:float = 1: 64 | get: 65 | return opacity 66 | set(value): 67 | if value == opacity: 68 | return 69 | opacity = value 70 | rebuild_layers = true 71 | 72 | @export_range(0, 4) var color_scalar:float = 1: 73 | get: 74 | return color_scalar 75 | set(value): 76 | if value == color_scalar: 77 | return 78 | color_scalar = value 79 | rebuild_layers = true 80 | 81 | @export var gradient:GradientTexture1D = preload("res://addons/volume_layered_shader/textures/purple_gradient_texture.tres"): 82 | get: 83 | return gradient 84 | set(value): 85 | if value == gradient: 86 | return 87 | gradient = value 88 | 89 | @export var exclusion_planes:Array[NodePath]: 90 | get: 91 | return exclusion_planes 92 | set(value): 93 | if value == exclusion_planes: 94 | return 95 | exclusion_planes = value 96 | rebuild_layers = true 97 | 98 | 99 | var rebuild_layers:bool = true 100 | var mesh_inst:MeshInstance3D 101 | 102 | func on_texture_changed(): 103 | rebuild_layers = true 104 | 105 | 106 | # Called when the node enters the scene tree for the first time. 107 | func _ready(): 108 | mesh_inst = MeshInstance3D.new() 109 | add_child(mesh_inst) 110 | 111 | var mesh:BoxMesh = BoxMesh.new() 112 | # mesh.flip_faces = true 113 | mesh.flip_faces = false 114 | mesh_inst.mesh = mesh 115 | 116 | var mat:Material = preload("res://addons/volume_layered_shader/materials/volume_layered_shader.tres").duplicate() 117 | mesh_inst.set_surface_override_material(0, mat) 118 | pass # Replace with function body. 119 | 120 | 121 | # Called every frame. 'delta' is the elapsed time since the previous frame. 122 | func _process(delta): 123 | #print("<0>") 124 | # if rebuild_layers: 125 | if !texture: 126 | return 127 | 128 | var x:float = texture.get_width() 129 | var y:float = texture.get_height() 130 | var z:float = texture.get_depth() 131 | 132 | #print("texture size ", Vector3i(x, y, z)) 133 | 134 | var basis:Basis = Basis.IDENTITY 135 | basis = basis * Basis.from_euler(Vector3(deg_to_rad(-90), 0, 0)) 136 | basis = basis * Basis.from_scale(Vector3(x, y, z) / min(x, y, z)) 137 | mesh_inst.transform = Transform3D(basis) 138 | 139 | var mat:ShaderMaterial = mesh_inst.get_surface_override_material(0) 140 | mat.set_shader_parameter("texture_volume", texture) 141 | mat.set_shader_parameter("layers", num_layers) 142 | mat.set_shader_parameter("opacity", opacity) 143 | mat.set_shader_parameter("color_scalar", color_scalar) 144 | mat.set_shader_parameter("gamma", gamma) 145 | mat.set_shader_parameter("gradient", gradient) 146 | 147 | var plane_count:int = 0 148 | var plane_list:PackedFloat32Array 149 | for node_path in exclusion_planes: 150 | if node_path.is_empty(): 151 | continue 152 | 153 | var node:Node = get_node(node_path) 154 | #print("node_path ", node_path) 155 | #print("node ", node) 156 | if node is Node3D: 157 | var xform:Transform3D = (node as Node3D).global_transform 158 | var p:Plane = Plane(xform.basis.z, xform.origin) 159 | plane_count += 1 160 | plane_list.append(p.x) 161 | plane_list.append(p.y) 162 | plane_list.append(p.z) 163 | plane_list.append(p.d) 164 | 165 | mat.set_shader_parameter("num_exclusion_planes", plane_count) 166 | mat.set_shader_parameter("exclusion_planes", plane_list) 167 | 168 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/glsl_shader_tools/glsl_shader_tool.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends RefCounted 26 | class_name GLSLShaderTool 27 | 28 | var rd:RenderingDevice 29 | 30 | func _init(rd:RenderingDevice): 31 | self.rd = rd 32 | 33 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/glsl_shader_tools/glsl_util.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends Resource 26 | class_name GLSLUtil 27 | 28 | var rd:RenderingDevice 29 | var mipmap_gen_rf_3d:MipmapGenerator_rf_3d 30 | var mipmap_gen_rgbaf_3d:MipmapGenerator_RGBAF_3D 31 | 32 | func _init(rd:RenderingDevice): 33 | self.rd = rd 34 | mipmap_gen_rf_3d = MipmapGenerator_rf_3d.new(rd) 35 | mipmap_gen_rgbaf_3d = MipmapGenerator_RGBAF_3D.new(rd) 36 | 37 | static func calc_mipmap_sizes(size:Vector3i)->Array[Vector3i]: 38 | var result:Array[Vector3i] 39 | calc_mipmap_sizes_recursive(size, result) 40 | return result 41 | 42 | static func calc_mipmap_sizes_recursive(size:Vector3i, result:Array[Vector3i]): 43 | result.append(size) 44 | 45 | if size == Vector3i.ONE: 46 | return 47 | 48 | var next_size:Vector3i = Vector3i(\ 49 | max(size.x >> 1, 1), \ 50 | max(size.y >> 1, 1), \ 51 | max(size.z >> 1, 1)) 52 | 53 | calc_mipmap_sizes_recursive(next_size, result) 54 | 55 | static func get_image_format(format:RenderingDevice.DataFormat)->Image.Format: 56 | match format: 57 | RenderingDevice.DATA_FORMAT_R32_SFLOAT: 58 | return Image.FORMAT_RF 59 | RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT: 60 | return Image.FORMAT_RGBAF 61 | RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM: 62 | return Image.FORMAT_RGBA8 63 | _: 64 | push_error("Unhandled format") 65 | return 0 66 | 67 | static func get_format_bytes_per_pixel(format:RenderingDevice.DataFormat)->int: 68 | match format: 69 | RenderingDevice.DATA_FORMAT_R32_SFLOAT: 70 | return 4 71 | RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT: 72 | return 16 73 | RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM: 74 | return 4 75 | _: 76 | push_error("Unhandled format") 77 | return 0 78 | 79 | func create_mipmaps_from_image_stack(img_list:Array[Image], format:RenderingDevice.DataFormat)->Array[Image]: 80 | var img_format:Image.Format = get_image_format(format) 81 | 82 | var data_buffer:PackedByteArray 83 | for img in img_list: 84 | var cur_format:Image.Format = img.get_format() 85 | if cur_format != img_format: 86 | push_error("Images must be in " + str(img_format) + " format") 87 | 88 | var local_data:PackedByteArray = img.get_data() 89 | data_buffer.append_array(local_data) 90 | 91 | 92 | var mipmap_img_list:Array[Image] 93 | var num_layers:int 94 | 95 | mipmap_img_list.append_array(img_list) 96 | 97 | if format == RenderingDevice.DATA_FORMAT_R32_SFLOAT: 98 | var images:Array[Image] = mipmap_gen_rf_3d.calculate(img_list) 99 | mipmap_img_list.append_array(images) 100 | if format == RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT: 101 | var images:Array[Image] = mipmap_gen_rgbaf_3d.calculate(img_list) 102 | mipmap_img_list.append_array(images) 103 | 104 | return mipmap_img_list 105 | 106 | func create_texture_image_from_image_stack_with_mipmaps(img_list:Array[Image], format:RenderingDevice.DataFormat, size:Vector3i, num_layers:int)->RID: 107 | var img_format:Image.Format = get_image_format(format) 108 | 109 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 110 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 111 | fmt_tex_out.width = size.x 112 | fmt_tex_out.height = size.y 113 | fmt_tex_out.depth = size.z 114 | fmt_tex_out.mipmaps = num_layers 115 | fmt_tex_out.format = format 116 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT \ 117 | | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT \ 118 | | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT 119 | 120 | var view:RDTextureView = RDTextureView.new() 121 | 122 | 123 | var data_buffer:PackedByteArray 124 | for img in img_list: 125 | var cur_format:Image.Format = img.get_format() 126 | if cur_format != img_format: 127 | push_error("Images must be in " + str(img_format) + " format") 128 | 129 | var local_data:PackedByteArray = img.get_data() 130 | data_buffer.append_array(local_data) 131 | 132 | var tex_layer_rid:RID = rd.texture_create(fmt_tex_out, view, [data_buffer]) 133 | 134 | return tex_layer_rid 135 | 136 | 137 | func create_texture_image_from_image_stack(img_list:Array[Image], format:RenderingDevice.DataFormat, gen_mipmaps:bool)->RID: 138 | var size:Vector3i = Vector3i(img_list[0].get_width(), img_list[0].get_height(), img_list.size()) 139 | var img_format:Image.Format = get_image_format(format) 140 | 141 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 142 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 143 | fmt_tex_out.width = size.x 144 | fmt_tex_out.height = size.y 145 | fmt_tex_out.depth = size.z 146 | fmt_tex_out.mipmaps = 1 147 | fmt_tex_out.format = format 148 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT \ 149 | | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT \ 150 | | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT 151 | var view:RDTextureView = RDTextureView.new() 152 | 153 | var data_buffer:PackedByteArray 154 | for img in img_list: 155 | var cur_format:Image.Format = img.get_format() 156 | if cur_format != img_format: 157 | push_error("Images must be in " + str(img_format) + " format") 158 | 159 | var local_data:PackedByteArray = img.get_data() 160 | data_buffer.append_array(local_data) 161 | 162 | var tex_layer_rid:RID = rd.texture_create(fmt_tex_out, view, [data_buffer]) 163 | 164 | if gen_mipmaps: 165 | var mipmap_img_list:Array[Image] 166 | var num_layers:int 167 | 168 | if format == RenderingDevice.DATA_FORMAT_R32_SFLOAT: 169 | num_layers = mipmap_gen_rf_3d.calc_mipmap_recursive(tex_layer_rid, size, mipmap_img_list) 170 | if format == RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT: 171 | num_layers = mipmap_gen_rgbaf_3d.calc_mipmap_recursive(tex_layer_rid, size, mipmap_img_list) 172 | 173 | 174 | rd.free_rid(tex_layer_rid) 175 | 176 | #Create mipmapped texture 177 | fmt_tex_out.mipmaps = num_layers + 1 178 | 179 | # var ggg = data_buffer.size() 180 | #data_buffer.clear() 181 | 182 | for i in mipmap_img_list.size(): 183 | var img:Image = mipmap_img_list[i] 184 | var cur_format:Image.Format = img.get_format() 185 | if cur_format != img_format: 186 | push_error("Images must be in " + str(img_format) + " format") 187 | 188 | # var iii = data_buffer.size() 189 | 190 | var local_data:PackedByteArray = img.get_data() 191 | data_buffer.append_array(local_data) 192 | 193 | # var hhh = data_buffer.size() 194 | 195 | #var jjj = data_buffer.size() 196 | 197 | 198 | tex_layer_rid = rd.texture_create(fmt_tex_out, view, [data_buffer]) 199 | 200 | 201 | return tex_layer_rid 202 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/glsl_shader_tools/mipmap_generator_rf_3d.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends GLSLShaderTool 26 | class_name MipmapGenerator_rf_3d 27 | 28 | #var rd:RenderingDevice 29 | var shader:RID 30 | 31 | func _init(rd:RenderingDevice): 32 | super._init(rd) 33 | #rd = RenderingServer.create_local_rendering_device() 34 | 35 | var shader_file:RDShaderFile = load("res://addons/volume_layered_shader/shaders/mipmap_generator_rf_3d.glsl") 36 | if !shader_file.base_error.is_empty(): 37 | push_error("Error loading shader\n", shader_file.base_error) 38 | return 39 | 40 | var shader_spirv:RDShaderSPIRV = shader_file.get_spirv() 41 | if !shader_spirv.compile_error_compute.is_empty(): 42 | push_error("Error compiling shader\n", shader_spirv.compile_error_compute) 43 | return 44 | 45 | shader = rd.shader_create_from_spirv(shader_spirv) 46 | 47 | 48 | func dispose(): 49 | rd.free_rid(shader) 50 | 51 | 52 | func calculate(img_list:Array[Image])->Array[Image]: 53 | var mipmap_img_list:Array[Image] 54 | var size:Vector3i = Vector3i(img_list[0].get_width(), img_list[0].get_height(), img_list.size()) 55 | 56 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 57 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 58 | fmt_tex_out.width = size.x 59 | fmt_tex_out.height = size.y 60 | fmt_tex_out.depth = size.z 61 | fmt_tex_out.mipmaps = 1 62 | fmt_tex_out.format = RenderingDevice.DATA_FORMAT_R32_SFLOAT 63 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 64 | var view := RDTextureView.new() 65 | 66 | var data_buffer:PackedByteArray 67 | for img in img_list: 68 | if img.get_format() != Image.FORMAT_RF: 69 | push_error("Images must be in RF format") 70 | 71 | var local_data:PackedByteArray = img.get_data() 72 | data_buffer.append_array(local_data) 73 | 74 | #var data_buffer_size:int = data_buffer.size() 75 | var tex_layer_rid:RID = rd.texture_create(fmt_tex_out, view, [data_buffer]) 76 | 77 | calc_mipmap_recursive(tex_layer_rid, size, mipmap_img_list) 78 | 79 | rd.free_rid(tex_layer_rid) 80 | 81 | return mipmap_img_list 82 | 83 | #var mip_img_idx:int = 0 84 | 85 | func calc_mipmap_recursive(tex_layer_rid:RID, size:Vector3i, mipmap_img_list:Array[Image])->int: 86 | 87 | if size.x == 1 && size.y == 1 && size.z == 1: 88 | return 0 89 | 90 | size = Vector3i(max(1, size.x >> 1), max(1, size.y >> 1), max(1, size.z >> 1)) 91 | 92 | var pipeline:RID = rd.compute_pipeline_create(shader) 93 | 94 | #Source image 95 | var source_tex_uniform:RDUniform = RDUniform.new() 96 | source_tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 97 | source_tex_uniform.binding = 0 98 | source_tex_uniform.add_id(tex_layer_rid) 99 | 100 | #Dest image 101 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 102 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 103 | fmt_tex_out.width = size.x 104 | fmt_tex_out.height = size.y 105 | fmt_tex_out.depth = size.z 106 | fmt_tex_out.format = RenderingDevice.DATA_FORMAT_R32_SFLOAT 107 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 108 | var view := RDTextureView.new() 109 | 110 | #var output_image:Image = Image.create(image_size.x, image_size.y, false, Image.FORMAT_RGBAF) 111 | var data:PackedByteArray 112 | data.resize(size.x * size.y * size.z * 4) 113 | data.fill(0) 114 | var dest_tex:RID = rd.texture_create(fmt_tex_out, view, [data]) 115 | 116 | 117 | var dest_tex_uniform:RDUniform = RDUniform.new() 118 | dest_tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 119 | dest_tex_uniform.binding = 1 120 | dest_tex_uniform.add_id(dest_tex) 121 | 122 | #### 123 | #Set uniforms 124 | var uniform_set = rd.uniform_set_create([source_tex_uniform, dest_tex_uniform], shader, 0) 125 | 126 | #Run the shader 127 | var compute_list = rd.compute_list_begin() 128 | rd.compute_list_bind_compute_pipeline(compute_list, pipeline) 129 | rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) 130 | # rd.compute_list_dispatch(compute_list, image_size.x / 8, image_size.y / 8, 1) 131 | @warning_ignore("integer_division") 132 | rd.compute_list_dispatch(compute_list, (size.x - 1) / 4 + 1, (size.y - 1) / 4 + 1, (size.z - 1) / 4 + 1) 133 | rd.compute_list_end() 134 | rd.submit() 135 | rd.sync() 136 | 137 | var byte_data:PackedByteArray = rd.texture_get_data(dest_tex, 0) 138 | # var float_data:PackedFloat32Array = byte_data.to_float32_array() 139 | 140 | var num_image_pixels:int = size.x * size.y 141 | for i in size.z: 142 | var image_data:PackedByteArray = byte_data.slice(num_image_pixels * 4 * i, num_image_pixels * 4 * (i + 1)) 143 | var img:Image = Image.create_from_data(size.x, size.y, false, Image.FORMAT_RF, image_data) 144 | mipmap_img_list.append(img) 145 | 146 | # img.save_png("art/mipmap/map_%d.png" % mip_img_idx) 147 | # mip_img_idx += 1 148 | #print_floats(float_data, 40) 149 | 150 | var ret_val:int = calc_mipmap_recursive(dest_tex, size, mipmap_img_list) 151 | 152 | rd.free_rid(pipeline) 153 | rd.free_rid(dest_tex) 154 | 155 | return ret_val + 1 156 | 157 | 158 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/glsl_shader_tools/mipmap_generator_rgba8_3d.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends GLSLShaderTool 26 | class_name MipmapGenerator_RGBA8_3D 27 | 28 | #var rd:RenderingDevice 29 | var shader:RID 30 | 31 | func _init(rd:RenderingDevice): 32 | super._init(rd) 33 | #rd = RenderingServer.create_local_rendering_device() 34 | 35 | var shader_file:RDShaderFile = load("res://shaders/mipmap_generator_rgba8_3d.glsl") 36 | if !shader_file.base_error.is_empty(): 37 | push_error("Error loading shader\n", shader_file.base_error) 38 | return 39 | 40 | var shader_spirv:RDShaderSPIRV = shader_file.get_spirv() 41 | if !shader_spirv.compile_error_compute.is_empty(): 42 | push_error("Error compiling shader\n", shader_spirv.compile_error_compute) 43 | return 44 | 45 | shader = rd.shader_create_from_spirv(shader_spirv) 46 | 47 | 48 | func dispose(): 49 | rd.free_rid(shader) 50 | 51 | 52 | func calculate(img_list:Array[Image])->Array[Image]: 53 | var mipmap_img_list:Array[Image] 54 | var size:Vector3i = Vector3i(img_list[0].get_width(), img_list[0].get_height(), img_list.size()) 55 | 56 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 57 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 58 | fmt_tex_out.width = size.x 59 | fmt_tex_out.height = size.y 60 | fmt_tex_out.depth = size.z 61 | fmt_tex_out.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM 62 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 63 | var view := RDTextureView.new() 64 | 65 | var data_buffer:PackedByteArray 66 | for img in img_list: 67 | data_buffer.append_array(img.get_data()) 68 | 69 | var tex_layer_rid:RID = rd.texture_create(fmt_tex_out, view, [data_buffer]) 70 | 71 | calc_mipmap_recursive(tex_layer_rid, size, mipmap_img_list) 72 | 73 | rd.free_rid(tex_layer_rid) 74 | 75 | return mipmap_img_list 76 | 77 | 78 | func calc_mipmap_recursive(tex_layer_rid:RID, size:Vector3i, mipmap_img_list:Array[Image]): 79 | 80 | if size.x == 1 && size.y == 1 && size.z == 1: 81 | return 82 | 83 | size = Vector3i(max(1, size.x >> 1), max(1, size.y >> 1), max(1, size.z >> 1)) 84 | 85 | var pipeline:RID = rd.compute_pipeline_create(shader) 86 | 87 | #Source image 88 | var source_tex_uniform:RDUniform = RDUniform.new() 89 | source_tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 90 | source_tex_uniform.binding = 0 91 | source_tex_uniform.add_id(tex_layer_rid) 92 | 93 | #Dest image 94 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 95 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 96 | fmt_tex_out.width = size.x 97 | fmt_tex_out.height = size.y 98 | fmt_tex_out.depth = size.z 99 | fmt_tex_out.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM 100 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 101 | var view := RDTextureView.new() 102 | 103 | #var output_image:Image = Image.create(image_size.x, image_size.y, false, Image.FORMAT_RGBAF) 104 | var data:PackedByteArray 105 | data.resize(size.x * size.y * size.z * 4) 106 | data.fill(0) 107 | var dest_tex:RID = rd.texture_create(fmt_tex_out, view, [data]) 108 | 109 | 110 | var dest_tex_uniform:RDUniform = RDUniform.new() 111 | dest_tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 112 | dest_tex_uniform.binding = 1 113 | dest_tex_uniform.add_id(dest_tex) 114 | 115 | #### 116 | #Set uniforms 117 | var uniform_set = rd.uniform_set_create([source_tex_uniform, dest_tex_uniform], shader, 0) 118 | 119 | #Run the shader 120 | var compute_list = rd.compute_list_begin() 121 | rd.compute_list_bind_compute_pipeline(compute_list, pipeline) 122 | rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) 123 | # rd.compute_list_dispatch(compute_list, image_size.x / 8, image_size.y / 8, 1) 124 | @warning_ignore("integer_division") 125 | rd.compute_list_dispatch(compute_list, (size.x - 1) / 4 + 1, (size.y - 1) / 4 + 1, (size.z - 1) / 4 + 1) 126 | rd.compute_list_end() 127 | rd.submit() 128 | rd.sync() 129 | 130 | var byte_data:PackedByteArray = rd.texture_get_data(dest_tex, 0) 131 | # var float_data:PackedFloat32Array = byte_data.to_float32_array() 132 | 133 | var num_image_pixels:int = size.x * size.y 134 | for i in size.z: 135 | var image_data:PackedByteArray = byte_data.slice(num_image_pixels * 4 * i, num_image_pixels * 4 * (i + 1)) 136 | var img:Image = Image.create_from_data(size.x, size.y, false, Image.FORMAT_RGBA8, image_data) 137 | mipmap_img_list.append(img) 138 | 139 | # img.save_png("art/mipmap/map_%d.png" % mip_img_idx) 140 | # mip_img_idx += 1 141 | #print_floats(float_data, 40) 142 | 143 | calc_mipmap_recursive(dest_tex, size, mipmap_img_list) 144 | 145 | rd.free_rid(pipeline) 146 | rd.free_rid(dest_tex) 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/glsl_shader_tools/mipmap_generator_rgbaf_3d.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends GLSLShaderTool 26 | class_name MipmapGenerator_RGBAF_3D 27 | 28 | #var rd:RenderingDevice 29 | var shader:RID 30 | 31 | func _init(rd:RenderingDevice): 32 | super._init(rd) 33 | #rd = RenderingServer.create_local_rendering_device() 34 | 35 | var shader_file:RDShaderFile = load("res://shaders/mipmap_generator_rgba8_3d.glsl") 36 | if !shader_file.base_error.is_empty(): 37 | push_error("Error loading shader\n", shader_file.base_error) 38 | return 39 | 40 | var shader_spirv:RDShaderSPIRV = shader_file.get_spirv() 41 | if !shader_spirv.compile_error_compute.is_empty(): 42 | push_error("Error compiling shader\n", shader_spirv.compile_error_compute) 43 | return 44 | 45 | shader = rd.shader_create_from_spirv(shader_spirv) 46 | 47 | 48 | func dispose(): 49 | rd.free_rid(shader) 50 | 51 | 52 | func calculate(img_list:Array[Image])->Array[Image]: 53 | var mipmap_img_list:Array[Image] 54 | var size:Vector3i = Vector3i(img_list[0].get_width(), img_list[0].get_height(), img_list.size()) 55 | 56 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 57 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 58 | fmt_tex_out.width = size.x 59 | fmt_tex_out.height = size.y 60 | fmt_tex_out.depth = size.z 61 | fmt_tex_out.format = RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT 62 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 63 | var view := RDTextureView.new() 64 | 65 | var data_buffer:PackedByteArray 66 | for img in img_list: 67 | data_buffer.append_array(img.get_data()) 68 | 69 | var tex_layer_rid:RID = rd.texture_create(fmt_tex_out, view, [data_buffer]) 70 | 71 | calc_mipmap_recursive(tex_layer_rid, size, mipmap_img_list) 72 | 73 | rd.free_rid(tex_layer_rid) 74 | 75 | return mipmap_img_list 76 | 77 | 78 | func calc_mipmap_recursive(tex_layer_rid:RID, size:Vector3i, mipmap_img_list:Array[Image])->int: 79 | 80 | if size.x == 1 && size.y == 1 && size.z == 1: 81 | return 0 82 | 83 | size = Vector3i(max(1, size.x >> 1), max(1, size.y >> 1), max(1, size.z >> 1)) 84 | 85 | var pipeline:RID = rd.compute_pipeline_create(shader) 86 | 87 | #Source image 88 | var source_tex_uniform:RDUniform = RDUniform.new() 89 | source_tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 90 | source_tex_uniform.binding = 0 91 | source_tex_uniform.add_id(tex_layer_rid) 92 | 93 | #Dest image 94 | var fmt_tex_out:RDTextureFormat = RDTextureFormat.new() 95 | fmt_tex_out.texture_type = RenderingDevice.TEXTURE_TYPE_3D 96 | fmt_tex_out.width = size.x 97 | fmt_tex_out.height = size.y 98 | fmt_tex_out.depth = size.z 99 | fmt_tex_out.format = RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT 100 | fmt_tex_out.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT 101 | var view := RDTextureView.new() 102 | 103 | #var output_image:Image = Image.create(image_size.x, image_size.y, false, Image.FORMAT_RGBAF) 104 | var data:PackedByteArray 105 | data.resize(size.x * size.y * size.z * 4 * 4) 106 | data.fill(0) 107 | var dest_tex:RID = rd.texture_create(fmt_tex_out, view, [data]) 108 | 109 | 110 | var dest_tex_uniform:RDUniform = RDUniform.new() 111 | dest_tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE 112 | dest_tex_uniform.binding = 1 113 | dest_tex_uniform.add_id(dest_tex) 114 | 115 | #### 116 | #Set uniforms 117 | var uniform_set = rd.uniform_set_create([source_tex_uniform, dest_tex_uniform], shader, 0) 118 | 119 | #Run the shader 120 | var compute_list = rd.compute_list_begin() 121 | rd.compute_list_bind_compute_pipeline(compute_list, pipeline) 122 | rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) 123 | @warning_ignore("integer_division") 124 | rd.compute_list_dispatch(compute_list, (size.x - 1) / 4 + 1, (size.y - 1) / 4 + 1, (size.z - 1) / 4 + 1) 125 | rd.compute_list_end() 126 | rd.submit() 127 | rd.sync() 128 | 129 | var byte_data:PackedByteArray = rd.texture_get_data(dest_tex, 0) 130 | 131 | var num_image_pixels:int = size.x * size.y 132 | for i in size.z: 133 | var image_data:PackedByteArray = byte_data.slice(num_image_pixels * 4 * 4 * i, num_image_pixels * 4 * 4 * (i + 1)) 134 | var img:Image = Image.create_from_data(size.x, size.y, false, Image.FORMAT_RGBAF, image_data) 135 | mipmap_img_list.append(img) 136 | 137 | # img.save_png("art/mipmap/map_%d.png" % mip_img_idx) 138 | # mip_img_idx += 1 139 | #print_floats(float_data, 40) 140 | 141 | var ret_val:int = calc_mipmap_recursive(dest_tex, size, mipmap_img_list) 142 | 143 | rd.free_rid(pipeline) 144 | rd.free_rid(dest_tex) 145 | 146 | return ret_val + 1 147 | 148 | 149 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/math/image_gradient.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends Resource 26 | class_name ImageGradient 27 | 28 | var gradients:Array[Vector3] 29 | #var gradients:PackedVector3Array 30 | var size:Vector3i 31 | 32 | func get_gradient(pos:Vector3)->Vector3: 33 | if pos.x < 0 || pos.x >= size.x \ 34 | || pos.y < 0 || pos.y >= size.y \ 35 | || pos.z < 0 || pos.z >= size.z: 36 | return Vector3.ZERO 37 | 38 | var idx:int = int(pos.x) + size.x * (int(pos.y) + size.y * int(pos.z)) 39 | return gradients[idx] 40 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/resources/cpu/npy_image_cpu_rf_Texture3d.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends ImageTexture3D 26 | class_name NpyImageCpuRFTexture3D 27 | 28 | @export_file("*.npy") var file_path:String: 29 | set(value): 30 | file_path = value 31 | reload_file() 32 | 33 | @export var frame:int = 0: 34 | set(value): 35 | frame = value 36 | reload_file() 37 | 38 | var max_frames:int = 1 39 | 40 | func _validate_property(property : Dictionary): 41 | #Do not write image data to resource file 42 | if property.name == "_images": 43 | property.usage = PROPERTY_USAGE_NONE 44 | 45 | func reload_file(): 46 | var loader:NpyLoader = NpyLoader.new() 47 | loader.load_file(file_path) 48 | 49 | max_frames = loader.size_w 50 | 51 | var img_list:Array[Image] = loader.load_image_stack(frame) 52 | var size:Vector3i = Vector3i(loader.size_x, loader.size_y, loader.size_z) 53 | 54 | #Generate mipmaps 55 | #var rd:RenderingDevice = RenderingServer.create_local_rendering_device() 56 | #var gen:MipmapGenerator_rf_3d = MipmapGenerator_rf_3d.new(rd) 57 | #var mipmap_images:Array[Image] = gen.calculate(img_list) 58 | 59 | create(Image.FORMAT_RF, size.x, size.y, size.z, false, img_list) 60 | 61 | changed.emit() 62 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/resources/cpu/zipped_image_archive_cpu_Texture3D.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends ImageTexture3D 26 | class_name ZippedImageArchiveCpuTexture3D 27 | 28 | @export_file("*.zip") var zip_file:String: 29 | get: 30 | return zip_file 31 | set(value): 32 | if value == zip_file: 33 | return 34 | zip_file = value 35 | load_image_from_zip(zip_file) 36 | 37 | func _validate_property(property : Dictionary): 38 | #Do not write image data to resource file 39 | if property.name == "_images": 40 | property.usage = PROPERTY_USAGE_NONE 41 | 42 | func add_mipmaps(img_width:int, img_height:int, img_depth:int, img_format:int, parent_images:Array[Image], img_list:Array[Image]): 43 | 44 | if img_width == 1 && img_height == 1 && img_depth == 1: 45 | return 46 | 47 | var mip_width:int = max(1, img_width >> 1) 48 | var mip_height:int = max(1, img_height >> 1) 49 | var mip_depth:int = max(1, img_depth >> 1) 50 | 51 | var mip_images:Array[Image] 52 | 53 | for z_idx in mip_depth: 54 | var image:Image = Image.create(mip_width, mip_height, false, img_format) 55 | 56 | img_list.append(image) 57 | mip_images.append(image) 58 | 59 | #print("adding mip idx %d w %d h %d img_depth %d" % [img_list.size(), mip_width, mip_height, img_depth]) 60 | 61 | for y_idx in mip_height: 62 | for x_idx in mip_width: 63 | var color:Color 64 | 65 | for zz in 2: 66 | var src_image:Image = parent_images[min(z_idx * 2 + zz, img_depth - 1)] 67 | 68 | for yy in 2: 69 | for xx in 2: 70 | color += src_image.get_pixel(min(x_idx * 2 + xx, img_width - 1), min(y_idx * 2 + yy, img_height - 1)) 71 | 72 | color /= 8 73 | image.set_pixel(x_idx, y_idx, color) 74 | 75 | add_mipmaps(mip_width, mip_height, mip_depth, img_format, mip_images, img_list) 76 | 77 | 78 | func load_image_from_zip(path:String): 79 | var reader:ZIPReader = ZIPReader.new() 80 | var err := reader.open(path) 81 | if err != OK: 82 | return null 83 | 84 | var img_width:int = -1 85 | var img_height:int = -1 86 | var img_format:int 87 | var img_list:Array[Image] 88 | 89 | for filename in reader.get_files(): 90 | if filename.ends_with(".png"): 91 | var buf:PackedByteArray = reader.read_file(filename) 92 | 93 | var image:Image = Image.new() 94 | image.load_png_from_buffer(buf) 95 | var cur_width:int = image.get_width() 96 | var cur_height:int = image.get_height() 97 | var cur_format:int = image.get_format() 98 | 99 | if img_width == -1 || (img_width == cur_width && img_height == cur_height && img_format == cur_format): 100 | #print("loading image ", img_list.size()) 101 | img_width = cur_width 102 | img_height = cur_height 103 | img_format = cur_format 104 | img_list.append(image) 105 | 106 | var img_depth:int = img_list.size() 107 | #print("num images ", img_list.size()) 108 | 109 | reader.close() 110 | 111 | if true: 112 | #TODO: Add mipmaps after https://github.com/godotengine/godot/issues/86423 is fixed 113 | create(img_format, img_width, img_height, img_depth, false, img_list) 114 | changed.emit() 115 | return 116 | 117 | 118 | add_mipmaps(img_width, img_height, img_list.size(), img_format, img_list, img_list) 119 | 120 | # for i in img_list.size(): 121 | # print("img %d %d %d" % [i, img_list[i].get_width(), img_list[i].get_height()]) 122 | 123 | create(img_format, img_width, img_height, img_depth, true, img_list) 124 | changed.emit() 125 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/resources/cpu/zipped_image_stack.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends Resource 26 | class_name ZippedImageStack 27 | 28 | @export_file("*.zip") var zip_file:String: 29 | get: 30 | return zip_file 31 | set(value): 32 | if value == zip_file: 33 | return 34 | zip_file = value 35 | load_image_from_zip(zip_file) 36 | 37 | var data:PackedFloat32Array 38 | var data_size:Vector3i 39 | 40 | var gradient:ImageGradient 41 | 42 | 43 | var supported_image_file_formats:Array[String] = [ 44 | "bmp", 45 | "dds", 46 | "exr", 47 | "hdr", 48 | "jpg", 49 | "jpeg", 50 | "png", 51 | "tga", 52 | "svg", 53 | "webp" 54 | ] 55 | 56 | func load_image_from_zip(path:String): 57 | var reader:ZIPReader = ZIPReader.new() 58 | var err := reader.open(path) 59 | if err != OK: 60 | return null 61 | 62 | var img_width:int = -1 63 | var img_height:int = -1 64 | var img_format:int 65 | var img_list:Array[Image] 66 | 67 | for filename in reader.get_files(): 68 | var suffix:String = filename.get_extension() 69 | if supported_image_file_formats.has(suffix): 70 | var buf:PackedByteArray = reader.read_file(filename) 71 | 72 | var image:Image = Image.new() 73 | image.load_png_from_buffer(buf) 74 | var cur_width:int = image.get_width() 75 | var cur_height:int = image.get_height() 76 | var cur_format:int = image.get_format() 77 | 78 | if img_width == -1 || (img_width == cur_width && img_height == cur_height && img_format == cur_format): 79 | #print("loading image ", img_list.size()) 80 | img_width = cur_width 81 | img_height = cur_height 82 | img_format = cur_format 83 | img_list.append(image) 84 | 85 | var img_depth:int = img_list.size() 86 | 87 | reader.close() 88 | 89 | #Store data 90 | data.clear() 91 | data_size = Vector3i(img_width, img_height, img_list.size()) 92 | 93 | for img in img_list: 94 | for y in img_height: 95 | for x in img_width: 96 | var col:Color = img.get_pixel(x, y) 97 | data.append(col.r) 98 | 99 | func get_cell_value(pos:Vector3i)->float: 100 | if pos.x < 0 || pos.x >= data_size.x \ 101 | || pos.y < 0 || pos.y >= data_size.y \ 102 | || pos.z < 0 || pos.z >= data_size.z: 103 | return 0 104 | 105 | var val:float = data[((pos.z * data_size.y) + pos.y) * data_size.x + pos.x] 106 | return val 107 | 108 | func get_gradient(pos:Vector3)->Vector3: 109 | var dx:float = get_cell_value(pos + Vector3(1, 0, 0)) - get_cell_value(pos + Vector3(-1, 0, 0)) 110 | var dy:float = get_cell_value(pos + Vector3(0, 1, 0)) - get_cell_value(pos + Vector3(0, -1, 0)) 111 | var dz:float = get_cell_value(pos + Vector3(0, 0, 1)) - get_cell_value(pos + Vector3(0, 0, -1)) 112 | 113 | return Vector3(dx, dy, dz) 114 | 115 | func calc_gradients()->ImageGradient: 116 | if gradient: 117 | return gradient 118 | 119 | # var grad_list:PackedVector3Array 120 | var grad_list:Array[Vector3] 121 | 122 | #Convolve with a simple 3x1 kernel for each dimension 123 | for k in data_size.z - 1: 124 | for j in data_size.y - 1: 125 | for i in data_size.x - 1: 126 | 127 | var dx:float = get_cell_value(Vector3i(i + 1, j, k)) - get_cell_value(Vector3i(i - 1, j, k)) 128 | var dy:float = get_cell_value(Vector3i(i, j + 1, k)) - get_cell_value(Vector3i(i, j - 1, k)) 129 | var dz:float = get_cell_value(Vector3i(i, j, k + 1)) - get_cell_value(Vector3i(i, j, k - 1)) 130 | 131 | var grad:Vector3 = Vector3(dx, dy, dz) 132 | if !grad.is_zero_approx(): 133 | pass 134 | grad_list.append(grad) 135 | 136 | gradient = ImageGradient.new() 137 | gradient.gradients = grad_list 138 | gradient.size = data_size 139 | return gradient 140 | 141 | 142 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/resources/gpu/npy_image_rf_Texture3D.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends ImageTexture3D 26 | class_name NpyImageRFTexture3D 27 | 28 | @export_file("*.npy") var file_path:String: 29 | set(value): 30 | file_path = value 31 | reload_file() 32 | 33 | @export var frame:int = 0: 34 | set(value): 35 | frame = value 36 | reload_file() 37 | 38 | var max_frames:int = 1 39 | 40 | func _validate_property(property : Dictionary): 41 | #Do not write image data to resource file 42 | if property.name == "_images": 43 | property.usage = PROPERTY_USAGE_NONE 44 | 45 | func reload_file(): 46 | var loader:NpyLoader = NpyLoader.new() 47 | loader.load_file(file_path) 48 | 49 | max_frames = loader.size_w 50 | 51 | var img_list:Array[Image] = loader.load_image_stack(frame) 52 | var size:Vector3i = Vector3i(loader.size_x, loader.size_y, loader.size_z) 53 | 54 | #Generate mipmaps 55 | var rd:RenderingDevice = RenderingServer.create_local_rendering_device() 56 | var gen:MipmapGenerator_rf_3d = MipmapGenerator_rf_3d.new(rd) 57 | var mipmap_images:Array[Image] = gen.calculate(img_list) 58 | 59 | # create(Image.FORMAT_RF, size.x, size.y, size.z, false, img_list) 60 | img_list.append_array(mipmap_images) 61 | 62 | var mesh_size_base = Vector3i(img_list[0].get_width(), \ 63 | img_list[0].get_height(), img_list.size()) 64 | var mipmap_sizes:Array[Vector3i] = GLSLUtil.calc_mipmap_sizes(mesh_size_base) 65 | 66 | # img_list = img_list.slice(0, img_list.size() - 1) 67 | 68 | create(Image.FORMAT_RF, size.x, size.y, size.z, true, img_list) 69 | 70 | changed.emit() 71 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/resources/gpu/npy_loader.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends Resource 26 | class_name NpyLoader 27 | 28 | var size_x:int = 1 29 | var size_y:int = 1 30 | var size_z:int = 1 31 | var size_w:int = 1 32 | var valid:bool = false 33 | var after_header:int 34 | var f:FileAccess 35 | var descr:String 36 | 37 | func round_up_to_64(v:int)->int: 38 | return (((v - 1) / 64) + 1) * 64 39 | 40 | func get_byte_size(descr:String)->int: 41 | match descr: 42 | "float: 68 | match descr: 69 | "void: 89 | match descr: 90 | "Array[Image]: 167 | var cur_frame:int = clamp(frame, 0, size_w - 1) 168 | var byte_size:int = get_byte_size(descr) 169 | 170 | var image_stack:Array[Image] 171 | 172 | f.seek(after_header + byte_size * cur_frame * size_x * size_y * size_z) 173 | 174 | for k in size_z: 175 | 176 | # if false: 177 | if descr == "Vector3i: 60 | if dirty: 61 | read_images_from_zip(zip_file) 62 | dirty = false 63 | return img_size 64 | 65 | func get_image_list()->Array[Image]: 66 | if dirty: 67 | read_images_from_zip(zip_file) 68 | dirty = false 69 | return img_list 70 | 71 | func read_images_from_zip(path:String): 72 | img_list.clear() 73 | img_size = Vector3i.ZERO 74 | if path.is_empty(): 75 | return 76 | 77 | var reader:ZIPReader = ZIPReader.new() 78 | var err := reader.open(path) 79 | if err != OK: 80 | return 81 | 82 | var img_width:int = -1 83 | var img_height:int = -1 84 | # var img_format:int = Image.FORMAT_RGBA8 85 | var img_format:int = Image.FORMAT_RF 86 | img_list.clear() 87 | 88 | for filename in reader.get_files(): 89 | var suffix:String = filename.get_extension() 90 | if supported_image_file_formats.has(suffix): 91 | var buf:PackedByteArray = reader.read_file(filename) 92 | 93 | var image:Image = Image.new() 94 | image.load_png_from_buffer(buf) 95 | var cur_width:int = image.get_width() 96 | var cur_height:int = image.get_height() 97 | var cur_format:int = image.get_format() 98 | if cur_format != img_format: 99 | image.convert(img_format) 100 | 101 | if img_width == -1 || (img_width == cur_width && img_height == cur_height): 102 | img_width = cur_width 103 | img_height = cur_height 104 | img_list.append(image) 105 | 106 | reader.close() 107 | 108 | var img_depth:int = img_list.size() 109 | img_size = Vector3i(img_width, img_height, img_depth) 110 | 111 | # var rd:RenderingDevice = RenderingServer.create_local_rendering_device() 112 | # var gen:MipmapGenerator_rf_3d = MipmapGenerator_rf_3d.new(rd) 113 | # var mipmap_images:Array[Image] = gen.calculate(img_list) 114 | # 115 | # img_list.append_array(mipmap_images) 116 | changed.emit() 117 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/scripts/resources/gpu/zipped_image_archive_rf_Texture3D.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | @tool 25 | extends ImageTexture3D 26 | class_name ZippedImageArchiveRFTexture3D 27 | 28 | @export var archive:ZippedImageArchive_RF_3D: 29 | get: 30 | return archive 31 | 32 | set(value): 33 | if value == archive: 34 | return 35 | 36 | if archive: 37 | archive.zipfile_changed.disconnect(on_archive_changed) 38 | 39 | archive = value 40 | 41 | if archive: 42 | archive.zipfile_changed.connect(on_archive_changed) 43 | load_image_from_archive(archive) 44 | 45 | func on_archive_changed(): 46 | load_image_from_archive(archive) 47 | 48 | func _validate_property(property : Dictionary): 49 | #Do not write image data to resource file 50 | if property.name == "_images": 51 | property.usage = PROPERTY_USAGE_NONE 52 | 53 | func load_image_from_archive(archive:ZippedImageArchive_RF_3D): 54 | var img_list:Array[Image] = archive.get_image_list().duplicate() 55 | var size:Vector3i = archive.get_size() 56 | #print("tex3d num img " + str(img_list.size())) 57 | 58 | #Generate mipmaps 59 | var rd:RenderingDevice = RenderingServer.create_local_rendering_device() 60 | var gen:MipmapGenerator_rf_3d = MipmapGenerator_rf_3d.new(rd) 61 | var mipmap_images:Array[Image] = gen.calculate(img_list) 62 | 63 | #create(Image.FORMAT_RF, size.x, size.y, size.z, false, img_list) 64 | 65 | img_list.append_array(mipmap_images) 66 | 67 | var mesh_size_base = Vector3i(img_list[0].get_width(), \ 68 | img_list[0].get_height(), img_list.size()) 69 | var mipmap_sizes:Array[Vector3i] = GLSLUtil.calc_mipmap_sizes(mesh_size_base) 70 | 71 | # img_list = img_list.slice(0, img_list.size() - 1) 72 | 73 | create(Image.FORMAT_RF, size.x, size.y, size.z, true, img_list) 74 | changed.emit() 75 | 76 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/mipmap_generator_rf_3d.glsl: -------------------------------------------------------------------------------- 1 | #[compute] 2 | 3 | /* 4 | * MIT License 5 | * 6 | * Copyright (c) 2023 Mark McKay 7 | * https://github.com/blackears/godot_volume_layers 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #version 450 29 | 30 | layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; 31 | 32 | layout(r32f, set = 0, binding = 0) uniform image3D source_image; 33 | //layout(rgba32f, set = 0, binding = 0) uniform image3D source_image; 34 | //layout(rgba8, set = 0, binding = 0) uniform image3D source_image; 35 | 36 | layout(r32f, set = 0, binding = 1) uniform image3D mipmap_image; 37 | //layout(rgba32f, set = 0, binding = 1) uniform image3D mipmap_image; 38 | //layout(rgba8, set = 0, binding = 1) uniform image3D mipmap_image; 39 | 40 | 41 | void main() { 42 | ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); 43 | 44 | float c000 = imageLoad(source_image, pos * 2 + ivec3(0, 0, 0)).r; 45 | float c100 = imageLoad(source_image, pos * 2 + ivec3(1, 0, 0)).r; 46 | float c010 = imageLoad(source_image, pos * 2 + ivec3(0, 1, 0)).r; 47 | float c110 = imageLoad(source_image, pos * 2 + ivec3(1, 1, 0)).r; 48 | float c001 = imageLoad(source_image, pos * 2 + ivec3(0, 0, 1)).r; 49 | float c101 = imageLoad(source_image, pos * 2 + ivec3(1, 0, 1)).r; 50 | float c011 = imageLoad(source_image, pos * 2 + ivec3(0, 1, 1)).r; 51 | float c111 = imageLoad(source_image, pos * 2 + ivec3(1, 1, 1)).r; 52 | 53 | float color = (c000 + c100 + c010 + c110 + c001 + c101 + c011 + c111) / 8; 54 | 55 | imageStore(mipmap_image, ivec3(gl_GlobalInvocationID.xyz), vec4(color.rrr, 1.0)); 56 | 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/mipmap_generator_rf_3d.glsl.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="glsl" 4 | type="RDShaderFile" 5 | uid="uid://i45xf76oexk7" 6 | path="res://.godot/imported/mipmap_generator_rf_3d.glsl-dc2b793119478af50810a63361dd9642.res" 7 | 8 | [deps] 9 | 10 | source_file="res://addons/volume_layered_shader/shaders/mipmap_generator_rf_3d.glsl" 11 | dest_files=["res://.godot/imported/mipmap_generator_rf_3d.glsl-dc2b793119478af50810a63361dd9642.res"] 12 | 13 | [params] 14 | 15 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/mipmap_generator_rgba8_3d.glsl: -------------------------------------------------------------------------------- 1 | #[compute] 2 | 3 | /* 4 | * MIT License 5 | * 6 | * Copyright (c) 2023 Mark McKay 7 | * https://github.com/blackears/godot_volume_layers 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #version 450 29 | 30 | layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; 31 | 32 | layout(rgba8, set = 0, binding = 0) readonly restrict uniform image3D source_image; 33 | 34 | layout(rgba8, set = 0, binding = 1) writeonly restrict uniform image3D mipmap_image; 35 | 36 | 37 | void main() { 38 | ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); 39 | 40 | vec4 c000 = imageLoad(source_image, pos * 2 + ivec3(0, 0, 0)); 41 | vec4 c100 = imageLoad(source_image, pos * 2 + ivec3(1, 0, 0)); 42 | vec4 c010 = imageLoad(source_image, pos * 2 + ivec3(0, 1, 0)); 43 | vec4 c110 = imageLoad(source_image, pos * 2 + ivec3(1, 1, 0)); 44 | vec4 c001 = imageLoad(source_image, pos * 2 + ivec3(0, 0, 1)); 45 | vec4 c101 = imageLoad(source_image, pos * 2 + ivec3(1, 0, 1)); 46 | vec4 c011 = imageLoad(source_image, pos * 2 + ivec3(0, 1, 1)); 47 | vec4 c111 = imageLoad(source_image, pos * 2 + ivec3(1, 1, 1)); 48 | 49 | vec4 color = (c000 + c100 + c010 + c110 + c001 + c101 + c011 + c111) / 8; 50 | 51 | imageStore(mipmap_image, ivec3(gl_GlobalInvocationID.xyz), color); 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/mipmap_generator_rgba8_3d.glsl.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="glsl" 4 | type="RDShaderFile" 5 | uid="uid://c8tsn8txn1e3k" 6 | path="res://.godot/imported/mipmap_generator_rgba8_3d.glsl-d6574f57d37b6edd9f6bac6d510763fd.res" 7 | 8 | [deps] 9 | 10 | source_file="res://addons/volume_layered_shader/shaders/mipmap_generator_rgba8_3d.glsl" 11 | dest_files=["res://.godot/imported/mipmap_generator_rgba8_3d.glsl-d6574f57d37b6edd9f6bac6d510763fd.res"] 12 | 13 | [params] 14 | 15 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/mipmap_generator_rgbaf_3d.glsl: -------------------------------------------------------------------------------- 1 | #[compute] 2 | 3 | /* 4 | * MIT License 5 | * 6 | * Copyright (c) 2023 Mark McKay 7 | * https://github.com/blackears/godot_volume_layers 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #version 450 29 | 30 | layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; 31 | 32 | layout(rgba32f, set = 0, binding = 0) readonly restrict uniform image3D source_image; 33 | 34 | layout(rgba32f, set = 0, binding = 1) writeonly restrict uniform image3D mipmap_image; 35 | 36 | 37 | void main() { 38 | ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); 39 | 40 | vec4 c000 = imageLoad(source_image, pos * 2 + ivec3(0, 0, 0)); 41 | vec4 c100 = imageLoad(source_image, pos * 2 + ivec3(1, 0, 0)); 42 | vec4 c010 = imageLoad(source_image, pos * 2 + ivec3(0, 1, 0)); 43 | vec4 c110 = imageLoad(source_image, pos * 2 + ivec3(1, 1, 0)); 44 | vec4 c001 = imageLoad(source_image, pos * 2 + ivec3(0, 0, 1)); 45 | vec4 c101 = imageLoad(source_image, pos * 2 + ivec3(1, 0, 1)); 46 | vec4 c011 = imageLoad(source_image, pos * 2 + ivec3(0, 1, 1)); 47 | vec4 c111 = imageLoad(source_image, pos * 2 + ivec3(1, 1, 1)); 48 | 49 | vec4 color = (c000 + c100 + c010 + c110 + c001 + c101 + c011 + c111) / 8; 50 | 51 | imageStore(mipmap_image, ivec3(gl_GlobalInvocationID.xyz), color); 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/mipmap_generator_rgbaf_3d.glsl.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="glsl" 4 | type="RDShaderFile" 5 | uid="uid://gqatv0xxvru7" 6 | path="res://.godot/imported/mipmap_generator_rgbaf_3d.glsl-d9a4dc03df190dbe564cb309f74b4b1d.res" 7 | 8 | [deps] 9 | 10 | source_file="res://addons/volume_layered_shader/shaders/mipmap_generator_rgbaf_3d.glsl" 11 | dest_files=["res://.godot/imported/mipmap_generator_rgbaf_3d.glsl-d9a4dc03df190dbe564cb309f74b4b1d.res"] 12 | 13 | [params] 14 | 15 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/shaders/volume_shader.gdshader: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Mark McKay 5 | * https://github.com/blackears/godot_volume_layers 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | shader_type spatial; 27 | render_mode unshaded, cull_front; 28 | 29 | const int MAX_EXCLUSION_PLANES = 4; 30 | 31 | uniform sampler3D texture_volume : source_color,filter_linear_mipmap,repeat_disable; 32 | uniform sampler2D gradient: filter_linear_mipmap,repeat_disable; 33 | uniform float zoom = 2; 34 | uniform int layers = 2; 35 | uniform float opacity = 1; 36 | uniform float gamma = 1; 37 | uniform float color_scalar = 1; 38 | uniform int num_exclusion_planes = 0; 39 | uniform vec4[MAX_EXCLUSION_PLANES] exclusion_planes; 40 | 41 | varying vec3 pos_local; 42 | varying vec3 pos_world; 43 | 44 | 45 | void vertex() { 46 | pos_local = VERTEX; 47 | pos_world = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; 48 | } 49 | 50 | vec3 isect_plane(vec3 view_origin, vec3 view_dir, vec3 plane_point, vec3 plane_dir) 51 | { 52 | float s = dot((plane_point - view_origin), plane_dir) / dot(view_dir, plane_dir); 53 | return s * view_dir + view_origin; 54 | } 55 | 56 | 57 | void fragment() { 58 | vec3 model_origin_world = MODEL_MATRIX[3].xyz; 59 | vec3 plane_norm = normalize(CAMERA_POSITION_WORLD - model_origin_world); 60 | 61 | vec4 col = vec4(0, 0, 0, 0); 62 | 63 | for (int layer_idx = 0; layer_idx < layers; ++layer_idx) 64 | { 65 | float layer_frac = float(layer_idx + 1) / float(layers + 1); 66 | vec3 plane_origin = model_origin_world + plane_norm * (layer_frac - .5) * 2.0; 67 | 68 | vec3 p_world = isect_plane(CAMERA_POSITION_WORLD, pos_world - CAMERA_POSITION_WORLD, plane_origin, plane_norm); 69 | 70 | bool plane_test_passed = true; 71 | for (int i = 0; i < min(num_exclusion_planes, MAX_EXCLUSION_PLANES); ++i) 72 | { 73 | if (dot(vec4(p_world, -1.0), exclusion_planes[i]) > 0.0) { 74 | plane_test_passed = false; 75 | break; 76 | } 77 | } 78 | if (!plane_test_passed) 79 | continue; 80 | 81 | mat4 inv_model_mtx = inverse(MODEL_MATRIX); 82 | vec3 p_local = (inv_model_mtx * vec4(p_world, 1.0)).xyz; 83 | 84 | 85 | vec3 tex_sample_pt = ((p_local * zoom) / 2.0 + vec3(.5, .5, .5)); 86 | vec4 layer_col = texture(texture_volume, tex_sample_pt); 87 | if (tex_sample_pt.x < 0.0 || tex_sample_pt.x > 1.0 88 | || tex_sample_pt.y < 0.0 || tex_sample_pt.y > 1.0 89 | || tex_sample_pt.z < 0.0 || tex_sample_pt.z > 1.0) 90 | continue; 91 | 92 | layer_col = texture(gradient, vec2(pow(layer_col.r, gamma), 0)); 93 | layer_col.a *= opacity; 94 | layer_col *= color_scalar; 95 | 96 | col = mix(col, layer_col, layer_col.a); 97 | } 98 | 99 | ALBEDO = col.xyz; 100 | ALPHA = col.a; 101 | } 102 | 103 | /* 104 | void fragment_2() { 105 | vec3 model_origin_world = MODEL_MATRIX[3].xyz; 106 | vec3 plane_norm = normalize(CAMERA_POSITION_WORLD - model_origin_world); 107 | 108 | vec4 col; 109 | 110 | for (int layer_idx = 0; layer_idx < layers; ++layer_idx) 111 | { 112 | float layer_frac = float(layer_idx + 1) / float(layers + 1); 113 | vec3 plane_origin = model_origin_world + plane_norm * (layer_frac - .5) * 2.0; 114 | 115 | vec3 p_world = isect_plane(CAMERA_POSITION_WORLD, pos_world - CAMERA_POSITION_WORLD, plane_origin, plane_norm); 116 | 117 | bool plane_test_passed = true; 118 | for (int i = 0; i < min(num_exclusion_planes, MAX_EXCLUSION_PLANES); ++i) 119 | { 120 | if (dot(vec4(p_world, -1.0), exclusion_planes[i]) > 0.0) { 121 | plane_test_passed = false; 122 | break; 123 | } 124 | } 125 | if (!plane_test_passed) 126 | continue; 127 | 128 | mat4 inv_model_mtx = inverse(MODEL_MATRIX); 129 | vec3 p_local = (inv_model_mtx * vec4(p_world, 1.0)).xyz; 130 | 131 | 132 | vec3 tex_sample_pt = ((p_local * zoom) / 2.0 + vec3(.5, .5, .5)); 133 | vec4 layer_col = texture(texture_volume, tex_sample_pt); 134 | if (tex_sample_pt.x < 0.0 || tex_sample_pt.x > 1.0 135 | || tex_sample_pt.y < 0.0 || tex_sample_pt.y > 1.0 136 | || tex_sample_pt.z < 0.0 || tex_sample_pt.z > 1.0) 137 | continue; 138 | 139 | layer_col = texture(gradient, vec2(pow(layer_col.r, gamma), 0)); 140 | layer_col.a *= opacity; 141 | layer_col *= color_scalar; 142 | 143 | col = mix(col, layer_col, layer_col.a); 144 | } 145 | 146 | ALBEDO = col.xyz; 147 | ALPHA = col.a; 148 | } 149 | */ -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/textures/low_values_grtadient.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://r1elp0kqouby"] 2 | 3 | [sub_resource type="Gradient" id="Gradient_ijs71"] 4 | offsets = PackedFloat32Array(0, 0.0406504, 0.235772, 1) 5 | colors = PackedColorArray(0.465653, 0.223287, 0.819413, 0, 0.480151, 0.244361, 0.824312, 0.0271318, 1, 1, 1, 1, 1, 1, 1, 1) 6 | 7 | [resource] 8 | gradient = SubResource("Gradient_ijs71") 9 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/textures/purple_gradient.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Gradient" format=3 uid="uid://bd4ldf822hhnh"] 2 | 3 | [resource] 4 | offsets = PackedFloat32Array(0, 0.0406504, 0.235772, 1) 5 | colors = PackedColorArray(0.465653, 0.223287, 0.819413, 0, 0.480151, 0.244361, 0.824312, 0.0271318, 1, 1, 1, 1, 1, 1, 1, 1) 6 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/textures/purple_gradient_texture.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://bb8bdgbv3lc4m"] 2 | 3 | [sub_resource type="Gradient" id="Gradient_22blr"] 4 | offsets = PackedFloat32Array(0, 0.0406504, 0.760163, 1) 5 | colors = PackedColorArray(0.465653, 0.223287, 0.819413, 0, 0.480151, 0.244361, 0.824312, 0.0271318, 0.711548, 0.598461, 0.973498, 1, 1, 1, 1, 1) 6 | 7 | [resource] 8 | gradient = SubResource("Gradient_22blr") 9 | -------------------------------------------------------------------------------- /godot/addons/volume_layered_shader/volume_layered_shader_addon.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | 5 | func _enter_tree(): 6 | # Initialization of the plugin goes here. 7 | add_custom_type("VolumeLayeredShader", "Node3D", 8 | preload("res://addons/volume_layered_shader/scenes/controls/volume_layered_shader.gd"), 9 | preload("res://addons/volume_layered_shader/art/icons/volume_layered.svg")) 10 | 11 | pass 12 | 13 | 14 | func _exit_tree(): 15 | # Clean-up of the plugin goes here. 16 | remove_custom_type("VolumeLayeredShader") 17 | pass 18 | -------------------------------------------------------------------------------- /godot/art/iguana/Iguana_png.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/godot/art/iguana/Iguana_png.zip -------------------------------------------------------------------------------- /godot/art/iguana_skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/godot/art/iguana_skull.png -------------------------------------------------------------------------------- /godot/art/iguana_skull.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b2biaw2iad7v7" 6 | path="res://.godot/imported/iguana_skull.png-0e268489ab0a7ae64c5d957983983f9d.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://art/iguana_skull.png" 14 | dest_files=["res://.godot/imported/iguana_skull.png-0e268489ab0a7ae64c5d957983983f9d.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /godot/art/noise_data_4d.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/godot/art/noise_data_4d.npy -------------------------------------------------------------------------------- /godot/data/iguana_zip_layers_cpu.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ImageTexture3D" script_class="ZippedImageArchiveCpuTexture3D" load_steps=2 format=3 uid="uid://gweqrs4nsgwa"] 2 | 3 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scripts/resources/cpu/zipped_image_archive_cpu_Texture3D.gd" id="1_y06yw"] 4 | 5 | [resource] 6 | script = ExtResource("1_y06yw") 7 | zip_file = "res://art/iguana/Iguana_png.zip" 8 | -------------------------------------------------------------------------------- /godot/data/iguana_zip_layers_gpu.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ImageTexture3D" script_class="ZippedImageArchiveRFTexture3D" load_steps=4 format=3 uid="uid://c4e7yo0jtg0qi"] 2 | 3 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scripts/resources/gpu/zipped_image_archive_rf_3d.gd" id="1_n7xid"] 4 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scripts/resources/gpu/zipped_image_archive_rf_Texture3D.gd" id="2_osirx"] 5 | 6 | [sub_resource type="Resource" id="Resource_abfcc"] 7 | script = ExtResource("1_n7xid") 8 | zip_file = "res://art/iguana/Iguana_png.zip" 9 | 10 | [resource] 11 | script = ExtResource("2_osirx") 12 | archive = SubResource("Resource_abfcc") 13 | -------------------------------------------------------------------------------- /godot/data/npy_noise_4d_cpu.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ImageTexture3D" script_class="NpyImageCpuRFTexture3D" load_steps=2 format=3 uid="uid://baqcwcckmp88m"] 2 | 3 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scripts/resources/cpu/npy_image_cpu_rf_Texture3d.gd" id="1_2xeub"] 4 | 5 | [resource] 6 | script = ExtResource("1_2xeub") 7 | file_path = "res://art/noise_data_4d.npy" 8 | frame = 0 9 | -------------------------------------------------------------------------------- /godot/data/npy_noise_4d_gpu.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ImageTexture3D" script_class="NpyImageRFTexture3D" load_steps=2 format=3 uid="uid://dktaosv5x5h14"] 2 | 3 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scripts/resources/gpu/npy_image_rf_Texture3d.gd" id="1_2ysug"] 4 | 5 | [resource] 6 | script = ExtResource("1_2ysug") 7 | file_path = "res://art/noise_data_4d.npy" 8 | frame = 8 9 | -------------------------------------------------------------------------------- /godot/doc/script.txt: -------------------------------------------------------------------------------- 1 | topics: 2 | ------ 3 | 4 | marching cubes algorithm 5 | 14 face arrangments taking rotations, reflections and inversions into account 6 | Root coloring: 0 7 | Root coloring: 1 8 | Root coloring: 3 9 | Root coloring: 6 10 | Root coloring: 7 11 | Root coloring: f 12 | Root coloring: 16 13 | Root coloring: 17 14 | Root coloring: 18 15 | Root coloring: 19 16 | Root coloring: 1b 17 | Root coloring: 1e 18 | Root coloring: 3c 19 | Root coloring: 69 20 | 21 | docs are INCORRRECT! 22 | add face coloring as well; inversions cannot be used for determining face set 23 | 22 possibilities 24 | 25 | 0 26 | 1 27 | 3 28 | 6 29 | 7 30 | f 31 | 16 32 | 17 33 | 18 34 | 19 35 | 1b 36 | 1e 37 | 1f 38 | 3c 39 | 3d 40 | 3f 41 | 69 42 | 6b 43 | 6f 44 | 7e 45 | 7f 46 | ff 47 | 48 | 49 | mipmap generation & compute shaders 50 | - mipmap necessary for 3d textures 51 | 52 | sobel operator & gradients 53 | 54 | nifti c++ wrapper 55 | -------------------------------------------------------------------------------- /godot/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /godot/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dbg7uqqw40u4p" 6 | path.s3tc="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://icon.svg" 15 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=false 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /godot/marching_cubes_bloyd_viewer0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/godot/marching_cubes_bloyd_viewer0.bin -------------------------------------------------------------------------------- /godot/marching_cubes_bloyd_viewer_bloyd0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/godot/marching_cubes_bloyd_viewer_bloyd0.bin -------------------------------------------------------------------------------- /godot/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Volume Layered Shader" 14 | run/main_scene="res://scenes/main_volume_layered_shader.tscn" 15 | config/features=PackedStringArray("4.3", "Mobile") 16 | config/icon="res://art/iguana_skull.png" 17 | 18 | [editor_plugins] 19 | 20 | enabled=PackedStringArray("res://addons/volume_layered_shader/plugin.cfg") 21 | -------------------------------------------------------------------------------- /godot/scenes/controls/trackball_tumbler.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | extends Node3D 25 | class_name TrackballTumbler 26 | 27 | @export var focus:Vector3 28 | 29 | var mouse_down_pos:Vector2 30 | #var dragging:bool = false 31 | enum DragStyle { NONE, TUMBLE, PAN } 32 | var drag_style:DragStyle = DragStyle.NONE 33 | 34 | var yaw_start:float 35 | var pitch_start:float 36 | var zoom_amount:float = .2 37 | 38 | func calc_pitch_yaw(pos:Vector3, focus_point:Vector3): 39 | var offset:Vector3 = pos - focus_point 40 | #var perp:Vector3 = offset.cross(Vector3.UP) 41 | 42 | var pitch = offset.angle_to(Vector3.UP) 43 | var proj_y:Vector3 = offset.project(Vector3.UP) 44 | var proj_xz:Vector3 = offset - proj_y 45 | var yaw = atan2(proj_xz.z, proj_xz.x) 46 | 47 | return {"pitch":pitch, "yaw":yaw} 48 | 49 | 50 | func _unhandled_input(event): 51 | #print(event) 52 | if false: 53 | if event.is_action("move_up"): 54 | position += basis.y 55 | focus += basis.y 56 | get_viewport().set_input_as_handled() 57 | elif event.is_action("move_down"): 58 | position += -basis.y 59 | focus += -basis.y 60 | get_viewport().set_input_as_handled() 61 | elif event.is_action("move_left"): 62 | position += -basis.x 63 | focus += -basis.x 64 | get_viewport().set_input_as_handled() 65 | elif event.is_action("move_right"): 66 | position += basis.x 67 | focus += basis.x 68 | get_viewport().set_input_as_handled() 69 | elif event.is_action("move_forward"): 70 | position += -basis.z 71 | focus += -basis.z 72 | get_viewport().set_input_as_handled() 73 | elif event.is_action("move_backward"): 74 | position += basis.z 75 | focus += basis.z 76 | get_viewport().set_input_as_handled() 77 | 78 | if event is InputEventMouseButton: 79 | if event.is_pressed(): 80 | if event.button_index == MOUSE_BUTTON_WHEEL_UP: 81 | var len:float = global_position.distance_to(focus) 82 | 83 | var new_len:float = len - zoom_amount 84 | var offset:Vector3 = global_position - focus 85 | var new_offset:Vector3 = offset.normalized() * new_len 86 | global_position = focus + new_offset 87 | look_at(focus) 88 | 89 | elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: 90 | var len:float = global_position.distance_to(focus) 91 | 92 | var new_len:float = len + zoom_amount 93 | var offset:Vector3 = global_position - focus 94 | var new_offset:Vector3 = offset.normalized() * new_len 95 | global_position = focus + new_offset 96 | look_at(focus) 97 | 98 | else: 99 | mouse_down_pos = event.position 100 | var result = calc_pitch_yaw(global_position, focus) 101 | pitch_start = result["pitch"] 102 | yaw_start = result["yaw"] 103 | if event.shift_pressed: 104 | drag_style = DragStyle.PAN 105 | else: 106 | drag_style = DragStyle.TUMBLE 107 | 108 | else: 109 | drag_style = DragStyle.NONE 110 | 111 | 112 | elif event is InputEventMouseMotion: 113 | if drag_style == DragStyle.TUMBLE: 114 | var offset:Vector2 = event.position - mouse_down_pos 115 | 116 | var len:float = global_position.distance_to(focus) 117 | var pitch_cur = clamp(pitch_start - offset.y * .01, - 0 / 2+ .01, PI - .01) 118 | var yaw_cur = yaw_start + offset.x * .01 119 | 120 | var proj_y_len:float = cos(pitch_cur) 121 | var proj_xz_len:float = sqrt(1 - proj_y_len * proj_y_len) 122 | 123 | var new_pos:Vector3 = Vector3(cos(yaw_cur) * proj_xz_len, proj_y_len, sin(yaw_cur) * proj_xz_len) * len 124 | 125 | global_position = focus + new_pos 126 | look_at(focus) 127 | 128 | if drag_style == DragStyle.PAN: 129 | pass 130 | 131 | 132 | 133 | # Called when the node enters the scene tree for the first time. 134 | func _ready(): 135 | calc_pitch_yaw(global_position, focus) 136 | pass # Replace with function body. 137 | 138 | 139 | # Called every frame. 'delta' is the elapsed time since the previous frame. 140 | func _process(delta): 141 | pass 142 | -------------------------------------------------------------------------------- /godot/scenes/controls/trackball_tumbler.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bk62samj5dspq"] 2 | 3 | [ext_resource type="Script" path="res://scenes/controls/trackball_tumbler.gd" id="1_wryfy"] 4 | 5 | [node name="trackball_tumbler" type="Node3D"] 6 | script = ExtResource("1_wryfy") 7 | -------------------------------------------------------------------------------- /godot/scenes/demos/noise_AnimationPlayer_demo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=12 format=3 uid="uid://cskbehatgnxbc"] 2 | 3 | [ext_resource type="Script" path="res://scenes/demos/noise_animation_player_demo.gd" id="1_03i3t"] 4 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scenes/controls/volume_layered_shader.gd" id="2_6gg1q"] 5 | [ext_resource type="ImageTexture3D" uid="uid://baqcwcckmp88m" path="res://data/npy_noise_4d_cpu.tres" id="3_wxwpb"] 6 | [ext_resource type="Texture2D" uid="uid://r1elp0kqouby" path="res://addons/volume_layered_shader/textures/low_values_grtadient.tres" id="4_v1mfp"] 7 | [ext_resource type="PackedScene" uid="uid://bk62samj5dspq" path="res://scenes/controls/trackball_tumbler.tscn" id="5_ue80t"] 8 | 9 | [sub_resource type="Environment" id="Environment_1nlpu"] 10 | background_mode = 1 11 | 12 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8cm72"] 13 | 14 | [sub_resource type="Theme" id="Theme_nfbml"] 15 | PanelContainer/styles/panel = SubResource("StyleBoxEmpty_8cm72") 16 | 17 | [sub_resource type="Animation" id="Animation_ba23v"] 18 | length = 0.001 19 | tracks/0/type = "value" 20 | tracks/0/imported = false 21 | tracks/0/enabled = true 22 | tracks/0/path = NodePath("VolumeLayeredShader:texture:frame") 23 | tracks/0/interp = 1 24 | tracks/0/loop_wrap = true 25 | tracks/0/keys = { 26 | "times": PackedFloat32Array(0), 27 | "transitions": PackedFloat32Array(1), 28 | "update": 1, 29 | "values": [0] 30 | } 31 | 32 | [sub_resource type="Animation" id="Animation_dq7xu"] 33 | resource_name = "new_animation" 34 | length = 4.0 35 | loop_mode = 1 36 | tracks/0/type = "value" 37 | tracks/0/imported = false 38 | tracks/0/enabled = true 39 | tracks/0/path = NodePath("VolumeLayeredShader:texture:frame") 40 | tracks/0/interp = 1 41 | tracks/0/loop_wrap = true 42 | tracks/0/keys = { 43 | "times": PackedFloat32Array(0, 4), 44 | "transitions": PackedFloat32Array(1, 1), 45 | "update": 0, 46 | "values": [0, 40] 47 | } 48 | 49 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_2mtww"] 50 | _data = { 51 | "RESET": SubResource("Animation_ba23v"), 52 | "new_animation": SubResource("Animation_dq7xu") 53 | } 54 | 55 | [node name="main" type="Node3D"] 56 | script = ExtResource("1_03i3t") 57 | 58 | [node name="VolumeLayeredShader" type="Node3D" parent="."] 59 | unique_name_in_owner = true 60 | script = ExtResource("2_6gg1q") 61 | texture = ExtResource("3_wxwpb") 62 | num_layers = 200 63 | gamma = 6.0 64 | color_scalar = 1.363 65 | gradient = ExtResource("4_v1mfp") 66 | exclusion_planes = Array[NodePath]([NodePath("../Marker3D"), NodePath("")]) 67 | 68 | [node name="Marker3D" type="Marker3D" parent="."] 69 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.76632) 70 | 71 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."] 72 | environment = SubResource("Environment_1nlpu") 73 | 74 | [node name="trackball_tumbler" parent="." instance=ExtResource("5_ue80t")] 75 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.52444) 76 | 77 | [node name="Camera3D" type="Camera3D" parent="trackball_tumbler"] 78 | 79 | [node name="DirectionalLight3D" type="DirectionalLight3D" parent="trackball_tumbler"] 80 | 81 | [node name="popup_load_file" type="FileDialog" parent="."] 82 | unique_name_in_owner = true 83 | title = "Open a File" 84 | size = Vector2i(600, 400) 85 | ok_button_text = "Open" 86 | file_mode = 0 87 | access = 2 88 | filters = PackedStringArray("*.zip", "*.npy") 89 | 90 | [node name="PanelContainer" type="PanelContainer" parent="."] 91 | offset_right = 355.0 92 | offset_bottom = 146.0 93 | theme = SubResource("Theme_nfbml") 94 | 95 | [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] 96 | layout_mode = 2 97 | 98 | [node name="bn_load" type="Button" parent="PanelContainer/VBoxContainer"] 99 | layout_mode = 2 100 | text = "Load volume" 101 | 102 | [node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 103 | layout_mode = 2 104 | 105 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer2"] 106 | layout_mode = 2 107 | text = "Num Layers 108 | " 109 | 110 | [node name="spin_num_layers" type="SpinBox" parent="PanelContainer/VBoxContainer/HBoxContainer2"] 111 | unique_name_in_owner = true 112 | layout_mode = 2 113 | min_value = 1.0 114 | value = 1.0 115 | allow_greater = true 116 | 117 | [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 118 | layout_mode = 2 119 | 120 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer"] 121 | layout_mode = 2 122 | text = "Gamma" 123 | 124 | [node name="slider_gamma" type="HSlider" parent="PanelContainer/VBoxContainer/HBoxContainer"] 125 | unique_name_in_owner = true 126 | layout_mode = 2 127 | size_flags_horizontal = 3 128 | max_value = 10.0 129 | step = 0.01 130 | value = 1.0 131 | 132 | [node name="AnimationPlayer" type="AnimationPlayer" parent="."] 133 | libraries = { 134 | "": SubResource("AnimationLibrary_2mtww") 135 | } 136 | autoplay = "new_animation" 137 | 138 | [connection signal="file_selected" from="popup_load_file" to="." method="_on_popup_load_file_file_selected"] 139 | [connection signal="pressed" from="PanelContainer/VBoxContainer/bn_load" to="." method="_on_bn_load_pressed"] 140 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/HBoxContainer2/spin_num_layers" to="." method="_on_spin_num_layers_value_changed"] 141 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/HBoxContainer/slider_gamma" to="." method="_on_slider_gamma_value_changed"] 142 | -------------------------------------------------------------------------------- /godot/scenes/demos/noise_animation_player_demo.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | extends Node3D 25 | 26 | @export_file("*.zip") var source_images:String 27 | 28 | func add_mipmaps(img_width:int, img_height:int, img_depth:int, img_format:int, parent_images:Array[Image], img_list:Array[Image]): 29 | 30 | if img_width == 1 && img_height == 1 && img_depth == 1: 31 | return 32 | 33 | var mip_width:int = max(1, img_width >> 1) 34 | var mip_height:int = max(1, img_height >> 1) 35 | var mip_depth:int = max(1, img_depth >> 1) 36 | 37 | var mip_images:Array[Image] 38 | 39 | for z_idx in mip_depth: 40 | var image:Image = Image.create(mip_width, mip_height, false, img_format) 41 | 42 | img_list.append(image) 43 | mip_images.append(image) 44 | 45 | #print("adding mip idx %d w %d h %d img_depth %d" % [img_list.size(), mip_width, mip_height, img_depth]) 46 | 47 | for y_idx in mip_height: 48 | for x_idx in mip_width: 49 | var color:Color 50 | 51 | for zz in 2: 52 | var src_image:Image = parent_images[min(z_idx * 2 + zz, img_depth - 1)] 53 | 54 | for yy in 2: 55 | for xx in 2: 56 | color += src_image.get_pixel(min(x_idx * 2 + xx, img_width - 1), min(y_idx * 2 + yy, img_height - 1)) 57 | 58 | color /= 8 59 | image.set_pixel(x_idx, y_idx, color) 60 | 61 | add_mipmaps(mip_width, mip_height, mip_depth, img_format, mip_images, img_list) 62 | 63 | 64 | 65 | func load_image_from_zip(path:String)->Texture3D: 66 | var reader:ZIPReader = ZIPReader.new() 67 | var err := reader.open(path) 68 | if err != OK: 69 | return null 70 | 71 | var img_width:int = -1 72 | var img_height:int = -1 73 | var img_format:int 74 | var img_list:Array[Image] 75 | 76 | for filename in reader.get_files(): 77 | if filename.ends_with(".png"): 78 | var buf:PackedByteArray = reader.read_file(filename) 79 | 80 | var image:Image = Image.new() 81 | image.load_png_from_buffer(buf) 82 | var cur_width:int = image.get_width() 83 | var cur_height:int = image.get_height() 84 | var cur_format:int = image.get_format() 85 | 86 | if img_width == -1 || (img_width == cur_width && img_height == cur_height && img_format == cur_format): 87 | print("loading image ", img_list.size()) 88 | img_width = cur_width 89 | img_height = cur_height 90 | img_format = cur_format 91 | img_list.append(image) 92 | 93 | #break 94 | 95 | var img_depth:int = img_list.size() 96 | print("num images ", img_list.size()) 97 | 98 | reader.close() 99 | 100 | add_mipmaps(img_width, img_height, img_list.size(), img_format, img_list, img_list) 101 | 102 | # for i in img_list.size(): 103 | # print("img %d %d %d" % [i, img_list[i].get_width(), img_list[i].get_height()]) 104 | 105 | var tex:ImageTexture3D = ImageTexture3D.new() 106 | tex.create(img_format, img_width, img_height, img_depth, true, img_list) 107 | # tex.create(img_format, img_width, img_height, img_depth, false, img_list) 108 | return tex 109 | 110 | # Called when the node enters the scene tree for the first time. 111 | func _ready(): 112 | %slider_gamma.value = %VolumeLayeredShader.gamma 113 | %spin_num_layers.value = %VolumeLayeredShader.num_layers 114 | 115 | pass # Replace with function body. 116 | 117 | 118 | 119 | 120 | func _on_bn_load_pressed(): 121 | %popup_load_file.popup_centered() 122 | 123 | 124 | func _on_popup_load_file_file_selected(path:String): 125 | if !FileAccess.file_exists(path): 126 | return 127 | 128 | if path.ends_with(".npy"): 129 | var tex:NpyImageCpuRFTexture3D = NpyImageCpuRFTexture3D.new() 130 | tex.file_path = path 131 | 132 | %VolumeLayeredShader.texture = tex 133 | 134 | elif path.ends_with(".zip"): 135 | var tex:ZippedImageArchiveCpuTexture3D = ZippedImageArchiveCpuTexture3D.new() 136 | tex.zip_file = path 137 | 138 | #var archive:ZippedImageArchive_RF_3D = ZippedImageArchive_RF_3D.new() 139 | #archive.zip_file = path 140 | # 141 | #var tex:ZippedImageArchiveRFTexture3D = ZippedImageArchiveRFTexture3D.new() 142 | #tex.archive = archive 143 | 144 | %VolumeLayeredShader.texture = tex 145 | 146 | 147 | 148 | func _on_slider_gamma_value_changed(value): 149 | %VolumeLayeredShader.gamma = value 150 | 151 | 152 | func _on_spin_num_layers_value_changed(value): 153 | %VolumeLayeredShader.num_layers = value 154 | 155 | 156 | func _on_spin_frame_value_changed(value: float) -> void: 157 | var tex = %VolumeLayeredShader.texture 158 | if tex && "frame" in tex: 159 | tex.frame = value 160 | 161 | var playing:bool = false 162 | var time_since_last_frame:float = 0 163 | 164 | func _on_bn_play_pressed() -> void: 165 | playing = true 166 | pass # Replace with function body. 167 | 168 | 169 | func _on_bn_stop_pressed() -> void: 170 | playing = false 171 | pass # Replace with function body. 172 | 173 | 174 | func _on_check_loop_toggled(toggled_on: bool) -> void: 175 | pass # Replace with function body. 176 | 177 | func _process(_delta): 178 | pass 179 | -------------------------------------------------------------------------------- /godot/scenes/demos/noise_demo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=10 format=3 uid="uid://bkirc72am4g2r"] 2 | 3 | [ext_resource type="Script" path="res://scenes/main_volume_layered_shader.gd" id="1_8wlyg"] 4 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scenes/controls/volume_layered_shader.gd" id="2_da4op"] 5 | [ext_resource type="ImageTexture3D" uid="uid://baqcwcckmp88m" path="res://data/npy_noise_4d_cpu.tres" id="3_5jpgh"] 6 | [ext_resource type="Texture2D" uid="uid://r1elp0kqouby" path="res://addons/volume_layered_shader/textures/low_values_grtadient.tres" id="4_8wwdj"] 7 | [ext_resource type="PackedScene" uid="uid://bk62samj5dspq" path="res://scenes/controls/trackball_tumbler.tscn" id="5_7v14r"] 8 | [ext_resource type="ButtonGroup" uid="uid://iqkw4bdceccp" path="res://scenes/player_buttons.tres" id="6_te0ra"] 9 | 10 | [sub_resource type="Environment" id="Environment_1nlpu"] 11 | background_mode = 1 12 | 13 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8cm72"] 14 | 15 | [sub_resource type="Theme" id="Theme_nfbml"] 16 | PanelContainer/styles/panel = SubResource("StyleBoxEmpty_8cm72") 17 | 18 | [node name="main" type="Node3D"] 19 | script = ExtResource("1_8wlyg") 20 | source_images = "res://art/iguana/Iguana_png.zip" 21 | 22 | [node name="VolumeLayeredShader" type="Node3D" parent="."] 23 | unique_name_in_owner = true 24 | script = ExtResource("2_da4op") 25 | texture = ExtResource("3_5jpgh") 26 | num_layers = 200 27 | gamma = 6.0 28 | color_scalar = 1.363 29 | gradient = ExtResource("4_8wwdj") 30 | exclusion_planes = Array[NodePath]([NodePath("../Marker3D"), NodePath("")]) 31 | 32 | [node name="Marker3D" type="Marker3D" parent="."] 33 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.76632) 34 | 35 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."] 36 | environment = SubResource("Environment_1nlpu") 37 | 38 | [node name="trackball_tumbler" parent="." instance=ExtResource("5_7v14r")] 39 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.52444) 40 | 41 | [node name="Camera3D" type="Camera3D" parent="trackball_tumbler"] 42 | 43 | [node name="DirectionalLight3D" type="DirectionalLight3D" parent="trackball_tumbler"] 44 | 45 | [node name="popup_load_file" type="FileDialog" parent="."] 46 | unique_name_in_owner = true 47 | title = "Open a File" 48 | size = Vector2i(600, 400) 49 | ok_button_text = "Open" 50 | file_mode = 0 51 | access = 2 52 | filters = PackedStringArray("*.zip", "*.npy") 53 | 54 | [node name="PanelContainer" type="PanelContainer" parent="."] 55 | offset_right = 355.0 56 | offset_bottom = 146.0 57 | theme = SubResource("Theme_nfbml") 58 | 59 | [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] 60 | layout_mode = 2 61 | 62 | [node name="bn_load" type="Button" parent="PanelContainer/VBoxContainer"] 63 | layout_mode = 2 64 | text = "Load volume" 65 | 66 | [node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 67 | layout_mode = 2 68 | 69 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer2"] 70 | layout_mode = 2 71 | text = "Num Layers 72 | " 73 | 74 | [node name="spin_num_layers" type="SpinBox" parent="PanelContainer/VBoxContainer/HBoxContainer2"] 75 | unique_name_in_owner = true 76 | layout_mode = 2 77 | min_value = 1.0 78 | value = 1.0 79 | allow_greater = true 80 | 81 | [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 82 | layout_mode = 2 83 | 84 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer"] 85 | layout_mode = 2 86 | text = "Gamma" 87 | 88 | [node name="slider_gamma" type="HSlider" parent="PanelContainer/VBoxContainer/HBoxContainer"] 89 | unique_name_in_owner = true 90 | layout_mode = 2 91 | size_flags_horizontal = 3 92 | max_value = 10.0 93 | step = 0.01 94 | value = 1.0 95 | 96 | [node name="GridContainer" type="GridContainer" parent="PanelContainer/VBoxContainer"] 97 | layout_mode = 2 98 | columns = 2 99 | 100 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/GridContainer"] 101 | layout_mode = 2 102 | text = "Frame" 103 | 104 | [node name="spin_frame" type="SpinBox" parent="PanelContainer/VBoxContainer/GridContainer"] 105 | unique_name_in_owner = true 106 | layout_mode = 2 107 | 108 | [node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/GridContainer"] 109 | layout_mode = 2 110 | text = "Frames per second" 111 | 112 | [node name="spin_fps" type="SpinBox" parent="PanelContainer/VBoxContainer/GridContainer"] 113 | unique_name_in_owner = true 114 | layout_mode = 2 115 | value = 30.0 116 | 117 | [node name="HBoxContainer4" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 118 | layout_mode = 2 119 | 120 | [node name="bn_play" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer4"] 121 | layout_mode = 2 122 | toggle_mode = true 123 | button_group = ExtResource("6_te0ra") 124 | text = "Play" 125 | 126 | [node name="bn_stop" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer4"] 127 | layout_mode = 2 128 | toggle_mode = true 129 | button_pressed = true 130 | button_group = ExtResource("6_te0ra") 131 | text = "Stop" 132 | 133 | [node name="check_loop" type="CheckBox" parent="PanelContainer/VBoxContainer/HBoxContainer4"] 134 | unique_name_in_owner = true 135 | layout_mode = 2 136 | button_pressed = true 137 | text = "Loop" 138 | 139 | [connection signal="file_selected" from="popup_load_file" to="." method="_on_popup_load_file_file_selected"] 140 | [connection signal="pressed" from="PanelContainer/VBoxContainer/bn_load" to="." method="_on_bn_load_pressed"] 141 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/HBoxContainer2/spin_num_layers" to="." method="_on_spin_num_layers_value_changed"] 142 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/HBoxContainer/slider_gamma" to="." method="_on_slider_gamma_value_changed"] 143 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/GridContainer/spin_frame" to="." method="_on_spin_frame_value_changed"] 144 | [connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer4/bn_play" to="." method="_on_bn_play_pressed"] 145 | [connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer4/bn_stop" to="." method="_on_bn_stop_pressed"] 146 | [connection signal="toggled" from="PanelContainer/VBoxContainer/HBoxContainer4/check_loop" to="." method="_on_check_loop_toggled"] 147 | -------------------------------------------------------------------------------- /godot/scenes/main_volume_layered_shader.gd: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Mark McKay 4 | # https://github.com/blackears/godot_volume_layers 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | extends Node3D 25 | 26 | @export_file("*.zip") var source_images:String 27 | 28 | func add_mipmaps(img_width:int, img_height:int, img_depth:int, img_format:int, parent_images:Array[Image], img_list:Array[Image]): 29 | 30 | if img_width == 1 && img_height == 1 && img_depth == 1: 31 | return 32 | 33 | var mip_width:int = max(1, img_width >> 1) 34 | var mip_height:int = max(1, img_height >> 1) 35 | var mip_depth:int = max(1, img_depth >> 1) 36 | 37 | var mip_images:Array[Image] 38 | 39 | for z_idx in mip_depth: 40 | var image:Image = Image.create(mip_width, mip_height, false, img_format) 41 | 42 | img_list.append(image) 43 | mip_images.append(image) 44 | 45 | #print("adding mip idx %d w %d h %d img_depth %d" % [img_list.size(), mip_width, mip_height, img_depth]) 46 | 47 | for y_idx in mip_height: 48 | for x_idx in mip_width: 49 | var color:Color 50 | 51 | for zz in 2: 52 | var src_image:Image = parent_images[min(z_idx * 2 + zz, img_depth - 1)] 53 | 54 | for yy in 2: 55 | for xx in 2: 56 | color += src_image.get_pixel(min(x_idx * 2 + xx, img_width - 1), min(y_idx * 2 + yy, img_height - 1)) 57 | 58 | color /= 8 59 | image.set_pixel(x_idx, y_idx, color) 60 | 61 | add_mipmaps(mip_width, mip_height, mip_depth, img_format, mip_images, img_list) 62 | 63 | 64 | 65 | func load_image_from_zip(path:String)->Texture3D: 66 | var reader:ZIPReader = ZIPReader.new() 67 | var err := reader.open(path) 68 | if err != OK: 69 | return null 70 | 71 | var img_width:int = -1 72 | var img_height:int = -1 73 | var img_format:int 74 | var img_list:Array[Image] 75 | 76 | for filename in reader.get_files(): 77 | if filename.ends_with(".png"): 78 | var buf:PackedByteArray = reader.read_file(filename) 79 | 80 | var image:Image = Image.new() 81 | image.load_png_from_buffer(buf) 82 | var cur_width:int = image.get_width() 83 | var cur_height:int = image.get_height() 84 | var cur_format:int = image.get_format() 85 | 86 | if img_width == -1 || (img_width == cur_width && img_height == cur_height && img_format == cur_format): 87 | print("loading image ", img_list.size()) 88 | img_width = cur_width 89 | img_height = cur_height 90 | img_format = cur_format 91 | img_list.append(image) 92 | 93 | #break 94 | 95 | var img_depth:int = img_list.size() 96 | print("num images ", img_list.size()) 97 | 98 | reader.close() 99 | 100 | add_mipmaps(img_width, img_height, img_list.size(), img_format, img_list, img_list) 101 | 102 | # for i in img_list.size(): 103 | # print("img %d %d %d" % [i, img_list[i].get_width(), img_list[i].get_height()]) 104 | 105 | var tex:ImageTexture3D = ImageTexture3D.new() 106 | tex.create(img_format, img_width, img_height, img_depth, true, img_list) 107 | # tex.create(img_format, img_width, img_height, img_depth, false, img_list) 108 | return tex 109 | 110 | # Called when the node enters the scene tree for the first time. 111 | func _ready(): 112 | %slider_gamma.value = %VolumeLayeredShader.gamma 113 | %spin_num_layers.value = %VolumeLayeredShader.num_layers 114 | 115 | var tex = %VolumeLayeredShader.texture 116 | if tex && "frame" in tex: 117 | if %spin_frame: 118 | %spin_frame.value = tex.frame 119 | 120 | 121 | pass # Replace with function body. 122 | 123 | 124 | 125 | 126 | func _on_bn_load_pressed(): 127 | %popup_load_file.popup_centered() 128 | 129 | 130 | func _on_popup_load_file_file_selected(path:String): 131 | if !FileAccess.file_exists(path): 132 | return 133 | 134 | if path.ends_with(".npy"): 135 | var tex:NpyImageCpuRFTexture3D = NpyImageCpuRFTexture3D.new() 136 | tex.file_path = path 137 | tex.frame = %spin_frame.value 138 | 139 | %VolumeLayeredShader.texture = tex 140 | 141 | elif path.ends_with(".zip"): 142 | var tex:ZippedImageArchiveCpuTexture3D = ZippedImageArchiveCpuTexture3D.new() 143 | tex.zip_file = path 144 | 145 | #var archive:ZippedImageArchive_RF_3D = ZippedImageArchive_RF_3D.new() 146 | #archive.zip_file = path 147 | # 148 | #var tex:ZippedImageArchiveRFTexture3D = ZippedImageArchiveRFTexture3D.new() 149 | #tex.archive = archive 150 | 151 | %VolumeLayeredShader.texture = tex 152 | 153 | 154 | 155 | func _on_slider_gamma_value_changed(value): 156 | %VolumeLayeredShader.gamma = value 157 | 158 | 159 | func _on_spin_num_layers_value_changed(value): 160 | %VolumeLayeredShader.num_layers = value 161 | 162 | 163 | func _on_spin_frame_value_changed(value: float) -> void: 164 | var tex = %VolumeLayeredShader.texture 165 | if tex && "frame" in tex: 166 | tex.frame = value 167 | 168 | var playing:bool = false 169 | var time_since_last_frame:float = 0 170 | 171 | func _on_bn_play_pressed() -> void: 172 | playing = true 173 | pass # Replace with function body. 174 | 175 | 176 | func _on_bn_stop_pressed() -> void: 177 | playing = false 178 | pass # Replace with function body. 179 | 180 | 181 | func _on_check_loop_toggled(toggled_on: bool) -> void: 182 | pass # Replace with function body. 183 | 184 | func _process(_delta): 185 | if playing: 186 | time_since_last_frame += _delta 187 | 188 | var sec_per_frame:float = 1.0 / %spin_fps.value 189 | if time_since_last_frame > sec_per_frame: 190 | time_since_last_frame = 0 191 | 192 | var frame:int = %spin_frame.value 193 | frame += 1 194 | 195 | var tex = %VolumeLayeredShader.texture 196 | 197 | if "max_frames" in tex: 198 | if %check_loop.button_pressed: 199 | frame = wrap(frame, 0, tex.max_frames) 200 | else: 201 | frame = min(frame, tex.max_frames) 202 | 203 | if "frame" in tex: 204 | tex.frame = frame 205 | 206 | %spin_frame.value = frame 207 | 208 | pass 209 | -------------------------------------------------------------------------------- /godot/scenes/main_volume_layered_shader.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=9 format=3 uid="uid://bxodnrolana07"] 2 | 3 | [ext_resource type="Script" path="res://scenes/main_volume_layered_shader.gd" id="1_1q2j1"] 4 | [ext_resource type="Script" path="res://addons/volume_layered_shader/scenes/controls/volume_layered_shader.gd" id="3_37taj"] 5 | [ext_resource type="ImageTexture3D" uid="uid://gweqrs4nsgwa" path="res://data/iguana_zip_layers_cpu.tres" id="3_msc3q"] 6 | [ext_resource type="PackedScene" uid="uid://bk62samj5dspq" path="res://scenes/controls/trackball_tumbler.tscn" id="3_w87qo"] 7 | [ext_resource type="ButtonGroup" uid="uid://iqkw4bdceccp" path="res://scenes/player_buttons.tres" id="5_lteic"] 8 | 9 | [sub_resource type="Environment" id="Environment_1nlpu"] 10 | background_mode = 1 11 | 12 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8cm72"] 13 | 14 | [sub_resource type="Theme" id="Theme_nfbml"] 15 | PanelContainer/styles/panel = SubResource("StyleBoxEmpty_8cm72") 16 | 17 | [node name="main" type="Node3D"] 18 | script = ExtResource("1_1q2j1") 19 | source_images = "res://art/iguana/Iguana_png.zip" 20 | 21 | [node name="VolumeLayeredShader" type="Node3D" parent="."] 22 | unique_name_in_owner = true 23 | script = ExtResource("3_37taj") 24 | texture = ExtResource("3_msc3q") 25 | num_layers = 200 26 | gamma = 2.2 27 | color_scalar = 1.363 28 | exclusion_planes = Array[NodePath]([NodePath("../Marker3D"), NodePath("")]) 29 | 30 | [node name="Marker3D" type="Marker3D" parent="."] 31 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.76632) 32 | 33 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."] 34 | environment = SubResource("Environment_1nlpu") 35 | 36 | [node name="trackball_tumbler" parent="." instance=ExtResource("3_w87qo")] 37 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.52444) 38 | 39 | [node name="Camera3D" type="Camera3D" parent="trackball_tumbler"] 40 | 41 | [node name="DirectionalLight3D" type="DirectionalLight3D" parent="trackball_tumbler"] 42 | 43 | [node name="popup_load_file" type="FileDialog" parent="."] 44 | unique_name_in_owner = true 45 | title = "Open a File" 46 | size = Vector2i(600, 400) 47 | ok_button_text = "Open" 48 | file_mode = 0 49 | access = 2 50 | filters = PackedStringArray("*.zip", "*.npy") 51 | 52 | [node name="PanelContainer" type="PanelContainer" parent="."] 53 | offset_right = 355.0 54 | offset_bottom = 146.0 55 | theme = SubResource("Theme_nfbml") 56 | 57 | [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] 58 | layout_mode = 2 59 | 60 | [node name="bn_load" type="Button" parent="PanelContainer/VBoxContainer"] 61 | layout_mode = 2 62 | text = "Load volume" 63 | 64 | [node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 65 | layout_mode = 2 66 | 67 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer2"] 68 | layout_mode = 2 69 | text = "Num Layers 70 | " 71 | 72 | [node name="spin_num_layers" type="SpinBox" parent="PanelContainer/VBoxContainer/HBoxContainer2"] 73 | unique_name_in_owner = true 74 | layout_mode = 2 75 | min_value = 1.0 76 | value = 1.0 77 | allow_greater = true 78 | 79 | [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 80 | layout_mode = 2 81 | 82 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer"] 83 | layout_mode = 2 84 | text = "Gamma" 85 | 86 | [node name="slider_gamma" type="HSlider" parent="PanelContainer/VBoxContainer/HBoxContainer"] 87 | unique_name_in_owner = true 88 | layout_mode = 2 89 | size_flags_horizontal = 3 90 | max_value = 10.0 91 | step = 0.01 92 | value = 1.0 93 | 94 | [node name="GridContainer" type="GridContainer" parent="PanelContainer/VBoxContainer"] 95 | layout_mode = 2 96 | columns = 2 97 | 98 | [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/GridContainer"] 99 | layout_mode = 2 100 | text = "Frame" 101 | 102 | [node name="spin_frame" type="SpinBox" parent="PanelContainer/VBoxContainer/GridContainer"] 103 | unique_name_in_owner = true 104 | layout_mode = 2 105 | 106 | [node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/GridContainer"] 107 | layout_mode = 2 108 | text = "Frames per second" 109 | 110 | [node name="spin_fps" type="SpinBox" parent="PanelContainer/VBoxContainer/GridContainer"] 111 | unique_name_in_owner = true 112 | layout_mode = 2 113 | value = 30.0 114 | 115 | [node name="HBoxContainer4" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] 116 | layout_mode = 2 117 | 118 | [node name="bn_play" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer4"] 119 | layout_mode = 2 120 | toggle_mode = true 121 | button_group = ExtResource("5_lteic") 122 | text = "Play" 123 | 124 | [node name="bn_stop" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer4"] 125 | layout_mode = 2 126 | toggle_mode = true 127 | button_pressed = true 128 | button_group = ExtResource("5_lteic") 129 | text = "Stop" 130 | 131 | [node name="check_loop" type="CheckBox" parent="PanelContainer/VBoxContainer/HBoxContainer4"] 132 | unique_name_in_owner = true 133 | layout_mode = 2 134 | button_pressed = true 135 | text = "Loop" 136 | 137 | [connection signal="file_selected" from="popup_load_file" to="." method="_on_popup_load_file_file_selected"] 138 | [connection signal="pressed" from="PanelContainer/VBoxContainer/bn_load" to="." method="_on_bn_load_pressed"] 139 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/HBoxContainer2/spin_num_layers" to="." method="_on_spin_num_layers_value_changed"] 140 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/HBoxContainer/slider_gamma" to="." method="_on_slider_gamma_value_changed"] 141 | [connection signal="value_changed" from="PanelContainer/VBoxContainer/GridContainer/spin_frame" to="." method="_on_spin_frame_value_changed"] 142 | [connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer4/bn_play" to="." method="_on_bn_play_pressed"] 143 | [connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer4/bn_stop" to="." method="_on_bn_stop_pressed"] 144 | -------------------------------------------------------------------------------- /godot/scenes/player_buttons.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ButtonGroup" format=3 uid="uid://iqkw4bdceccp"] 2 | 3 | [resource] 4 | -------------------------------------------------------------------------------- /godot/testing/create_noise_volume.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import opensimplex 3 | from PIL import Image 4 | 5 | feature_size = 10 6 | size_x = 70 7 | size_y = 60 8 | size_z = 50 9 | size_w = 60 10 | 11 | #opensimplex.seed(13) 12 | #opensimplex.random_seed() 13 | 14 | #ix, iy = rng.random(2), rng.random(2) 15 | ix = np.arange(0, feature_size, feature_size / size_x) 16 | iy = np.arange(0, feature_size, feature_size / size_y) 17 | iz = np.arange(0, feature_size, feature_size / size_z) 18 | iw = np.arange(0, feature_size, feature_size / size_w) 19 | 20 | #print(ix, iy) 21 | 22 | arr = opensimplex.noise4array(ix, iy, iz, iw) 23 | arr = (arr + 1) * .5 24 | 25 | print("Noise volume calculated") 26 | 27 | # im_data = (arr[:, :, 0, 0] * 255).astype(np.uint8) 28 | 29 | # print(im_data) 30 | 31 | # im = Image.fromarray(im_data) 32 | # im.save("noise.png") 33 | 34 | np.save('noise_data_4d.npy', arr.astype(np.float16)) 35 | 36 | -------------------------------------------------------------------------------- /sources.txt: -------------------------------------------------------------------------------- 1 | Iguana test data 2 | https://github.com/neurolabusc/niivue-images/blob/main/Iguana.nii.gz 3 | 4 | Online converter 5 | https://www.onlineconverter.com/nifti-to-png 6 | 7 | Carleton discussion 8 | https://www.cs.carleton.edu/cs_comps/0405/shape/marching_cubes.html -------------------------------------------------------------------------------- /video/models/cube.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blackears/godot_volume_layers/53f82e2fc1278d71a0df2a8c89371da122ea6977/video/models/cube.blend --------------------------------------------------------------------------------