├── .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 | [](https://www.youtube.com/watch?v=1IvKWIieZPM)
4 | [](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 | 
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 | 
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 | [](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 |
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
--------------------------------------------------------------------------------