Release/v6 #27
2
.gitignore
vendored
|
@ -13,6 +13,8 @@ data_*/
|
|||
mono_crash.*.json
|
||||
|
||||
# Builds
|
||||
*.x86_64
|
||||
godot.*.template_release.*
|
||||
dist/*
|
||||
!dist/.gitkeep
|
||||
|
||||
|
|
19
README.md
|
@ -27,21 +27,28 @@ The image file will be read and available as the `TEXTURE` variable.
|
|||
#### Load additional images
|
||||
|
||||
```glsl
|
||||
//!load <name> <filepath>
|
||||
//!load+ <name> <filepath>
|
||||
|
||||
uniform sampler2D <name>;
|
||||
```
|
||||
|
||||
Have a look at the `mix.gdshader` example:
|
||||
Have a look at the `place_texture.gdshader` example.
|
||||
|
||||
## Shaderlib
|
||||
|
||||
This repo comes with a (still small) shader library including pre-written functions and more.
|
||||
Have a look at the `shaderlib` folder.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```glsl
|
||||
shader_type canvas_item;
|
||||
|
||||
#include "res://shaderlib/hsv.gdshaderinc"
|
||||
|
||||
//!load ./swamp.jpg
|
||||
|
||||
//!load+ img2 ./overlay.jpg
|
||||
uniform sampler2D img2: repeat_enable, filter_nearest;
|
||||
|
||||
void fragment() {
|
||||
COLOR = mix(COLOR, texture(img2, UV), .2);
|
||||
COLOR = hsv_offset(COLOR, 0.32, 0.2, 0.0);
|
||||
}
|
||||
```
|
||||
|
|
80
assets/error.svg
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="error.svg"
|
||||
inkscape:export-filename="error.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
showgrid="true"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="12.28598"
|
||||
inkscape:cy="16.108776"
|
||||
inkscape:window-width="1854"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="66"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid9"
|
||||
originx="0"
|
||||
originy="0" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="-19.109422"
|
||||
y="15.119667"
|
||||
width="23.697131"
|
||||
height="28.949477"
|
||||
id="rect4519" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
style="display:none;opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:#ff3815;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 4,25 H 26 L 15,6 Z"
|
||||
id="path11345"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<circle
|
||||
style="opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:none;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="path15121"
|
||||
cx="15"
|
||||
cy="15"
|
||||
r="13" />
|
||||
<g
|
||||
aria-label="!"
|
||||
transform="matrix(0.80238423,0,0,0.80238423,20.811527,-5.9033718)"
|
||||
id="text4517"
|
||||
style="font-size:24px;white-space:pre;shape-inside:url(#rect4519);display:inline;fill:#ffffff;fill-opacity:1">
|
||||
<path
|
||||
d="m -5.6133052,29.164717 h -3.264 L -9.5630195,16.081288 h 4.6354286 z m -4.0045715,4.580571 q 0,-1.261715 0.6857143,-1.755429 0.6857143,-0.521142 1.6731429,-0.521142 0.96,0 1.6457143,0.521142 0.6857143,0.493714 0.6857143,1.755429 0,1.206857 -0.6857143,1.755429 -0.6857143,0.521142 -1.6457143,0.521142 -0.9874286,0 -1.6731429,-0.521142 -0.6857143,-0.548572 -0.6857143,-1.755429 z"
|
||||
style="font-weight:bold;-inkscape-font-specification:'sans-serif Bold';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:1.14286"
|
||||
id="path4674" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
37
assets/error.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://04iv1gogpuhu"
|
||||
path="res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/error.svg"
|
||||
dest_files=["res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=2.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
|
@ -3,15 +3,15 @@
|
|||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://kqwc4avs2xdp"
|
||||
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
|
||||
path="res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
|
||||
source_file="res://assets/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"]
|
||||
|
||||
[params]
|
||||
|
81
assets/okay.svg
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="success.svg"
|
||||
inkscape:export-filename="error.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
showgrid="true"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="3.314563"
|
||||
inkscape:cy="14.385204"
|
||||
inkscape:window-width="1854"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="66"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid9"
|
||||
originx="0"
|
||||
originy="0" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="30"
|
||||
height="30"
|
||||
id="rect15390" />
|
||||
<rect
|
||||
x="-19.109422"
|
||||
y="15.119667"
|
||||
width="23.697131"
|
||||
height="28.949477"
|
||||
id="rect4519" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
style="display:none;opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:#ff3815;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 4,25 H 26 L 15,6 Z"
|
||||
id="path11345"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<circle
|
||||
style="opacity:0.75;fill:#15ff1e;fill-opacity:1;stroke:none;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="path15121"
|
||||
cx="15"
|
||||
cy="15"
|
||||
r="13" />
|
||||
<path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8.1284306,14.403599 5.2374004,4.840883 8.39167,-9.6564053"
|
||||
id="path20589"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
37
assets/okay.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://m1omb6g45vst"
|
||||
path="res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/okay.svg"
|
||||
dest_files=["res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=2.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
21
build-template/Containerfile
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
MAINTAINER ChaoticByte
|
||||
|
||||
# Using Ubuntu 20.04
|
||||
FROM docker.io/ubuntu:focal AS os-base
|
||||
|
||||
# https://docs.godotengine.org/en/stable/contributing/development/compiling/compiling_for_linuxbsd.html
|
||||
|
||||
RUN apt-get update
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq build-essential scons pkg-config libx11-dev libxcursor-dev libxinerama-dev libgl1-mesa-dev libglu1-mesa-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev libwayland-dev
|
||||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq git
|
||||
|
||||
FROM os-base AS clone-src
|
||||
|
||||
RUN git clone https://github.com/godotengine/godot.git -b 4.3-stable /godot-src
|
||||
|
||||
FROM clone-src
|
||||
|
||||
WORKDIR /godot-src
|
||||
ENTRYPOINT scons platform=linuxbsd target=template_release lto=full optimize=size disable_3d=yes module_text_server_adv_enabled=no module_text_server_fb_enabled=yes module_basis_universal_enabled=no module_csg_enabled=no module_dds_enabled=no module_enet_enabled=no module_gridmap_enabled=no module_hdr_enabled=no module_jsonrpc_enabled=no module_ktx_enabled=no module_mbedtls_enabled=no module_meshoptimizer_enabled=no module_minimp3_enabled=no module_mobile_vr_enabled=no module_msdfgen_enabled=no module_multiplayer_enabled=no module_navigation_enabled=no module_ogg_enabled=no module_openxr_enabled=no module_raycast_enabled=no module_squish_enabled=no module_svg_enabled=no module_tga_enabled=no module_theora_enabled=no module_tinyexr_enabled=no module_upnp_enabled=no module_vhacd_enabled=no module_vorbis_enabled=no module_webrtc_enabled=no module_websocket_enabled=no module_webxr_enabled=no module_zip_enabled=no arch=x86_64 && strip bin/godot.linuxbsd.template_release.x86_64
|
29
build-template/build.sh
Executable file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
function log {
|
||||
echo -e "\033[1;36m* $@\033[0m"
|
||||
}
|
||||
|
||||
log
|
||||
log "Fragmented - Godot Build Template Builder"
|
||||
log
|
||||
|
||||
cd $(dirname $0)
|
||||
log Switched to $(pwd)
|
||||
|
||||
tmpsuffix=$(date +%s%N)
|
||||
image_name=fragmented-godot-template-builder
|
||||
container_name=${image_name}-${tmpsuffix}
|
||||
output_file=godot.linuxbsd.template_release.x86_64
|
||||
|
||||
log Building image ${image_name} ...
|
||||
buildah build -t ${image_name}
|
||||
log Building godot build template with container ${container_name} ...
|
||||
podman run --name ${container_name} localhost/${image_name}:latest
|
||||
log Copying ${output_file} from container to $(realpath ./${output_file})
|
||||
podman cp ${container_name}:/godot-src/bin/${output_file} ./${output_file}
|
||||
log Removing container ${container_name}
|
||||
podman container rm ${container_name}
|
||||
log Done :D
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
# Example Images
|
||||
|
||||
- swamp.jpg by [clfr21](https://pixabay.com/de/users/clfr21-6530007/)
|
||||
- overlay.jpg by [Humusak](https://pixabay.com/de/users/humusak-137455/)
|
||||
- swamp.jpg by [clfr21 on Pixabay](https://pixabay.com/de/users/clfr21-6530007/)
|
||||
- grass.png by [GDJ on Pixabay](https://pixabay.com/users/gdj-1086657/)
|
||||
|
|
BIN
examples/grass.png
Normal file
After Width: | Height: | Size: 150 KiB |
|
@ -2,16 +2,16 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://8101nnc8cx4o"
|
||||
path="res://.godot/imported/overlay.jpg-b6d13e0def59877969c9eda02fa4244e.ctex"
|
||||
uid="uid://c1mh1d2f3u4ju"
|
||||
path="res://.godot/imported/grass.png-30af7213ed2d70b810b4ae788314a9e9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://examples/overlay.jpg"
|
||||
dest_files=["res://.godot/imported/overlay.jpg-b6d13e0def59877969c9eda02fa4244e.ctex"]
|
||||
source_file="res://examples/grass.png"
|
||||
dest_files=["res://.godot/imported/grass.png-30af7213ed2d70b810b4ae788314a9e9.ctex"]
|
||||
|
||||
[params]
|
||||
|
9
examples/hsv.gdshader
Normal file
|
@ -0,0 +1,9 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
#include "res://shaderlib/hsv.gdshaderinc"
|
||||
|
||||
//!load ./swamp.jpg
|
||||
|
||||
void fragment() {
|
||||
COLOR = hsv_multiply(hsv_offset(COLOR, -0.3, 0.9, 0.0), 1.0, 0.44, 1.0);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
//!load ./swamp.jpg
|
||||
|
||||
//!load+ img2 ./overlay.jpg
|
||||
uniform sampler2D img2: repeat_enable, filter_nearest;
|
||||
|
||||
void fragment() {
|
||||
COLOR = mix(COLOR, texture(img2, UV), .2);
|
||||
}
|
Before Width: | Height: | Size: 896 KiB |
15
examples/place_texture.gdshader
Normal file
|
@ -0,0 +1,15 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
#include "res://shaderlib/transform.gdshaderinc"
|
||||
#include "res://shaderlib/transparency.gdshaderinc"
|
||||
|
||||
//!load ./swamp.jpg
|
||||
//!load+ img2 ./grass.png
|
||||
|
||||
uniform sampler2D img2: repeat_disable, filter_nearest;
|
||||
|
||||
void fragment() {
|
||||
vec4 grass = place_texture(img2, UV, TEXTURE_PIXEL_SIZE, vec2(0, .47), vec2(1));
|
||||
grass.rgb += (vec3(0.02, 0.07, 0.1) - ((UV.y - .8) * 0.15)); // color correction
|
||||
COLOR = alpha_blend(COLOR, grass);
|
||||
}
|
|
@ -1,29 +1,29 @@
|
|||
[preset.0]
|
||||
|
||||
name="Linux/X11"
|
||||
platform="Linux/X11"
|
||||
platform="Linux"
|
||||
runnable=true
|
||||
advanced_options=false
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
exclude_filter="screenshot.png, examples/*"
|
||||
export_path="dist/Fragmented.x86_64"
|
||||
encryption_include_filters=""
|
||||
encryption_exclude_filters=""
|
||||
encrypt_pck=false
|
||||
encrypt_directory=false
|
||||
script_export_mode=2
|
||||
|
||||
[preset.0.options]
|
||||
|
||||
custom_template/debug=""
|
||||
custom_template/release=""
|
||||
custom_template/release="./build-template/godot.linuxbsd.template_release.x86_64"
|
||||
debug/export_console_wrapper=1
|
||||
binary_format/embed_pck=true
|
||||
texture_format/bptc=true
|
||||
texture_format/s3tc=true
|
||||
texture_format/etc=false
|
||||
texture_format/etc2=false
|
||||
texture_format/s3tc_bptc=true
|
||||
texture_format/etc2_astc=false
|
||||
binary_format/architecture="x86_64"
|
||||
ssh_remote_deploy/enabled=false
|
||||
ssh_remote_deploy/host="user@host_ip"
|
||||
|
@ -37,3 +37,7 @@ unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
|
|||
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
|
||||
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
|
||||
rm -rf \"{temp_dir}\""
|
||||
texture_format/bptc=true
|
||||
texture_format/s3tc=true
|
||||
texture_format/etc=false
|
||||
texture_format/etc2=false
|
||||
|
|
|
@ -11,22 +11,23 @@ config_version=5
|
|||
[application]
|
||||
|
||||
config/name="Fragmented"
|
||||
config/version="v5.0"
|
||||
config/version="v6.0"
|
||||
run/main_scene="res://scenes/main.tscn"
|
||||
config/features=PackedStringArray("4.3", "Mobile")
|
||||
run/low_processor_mode=true
|
||||
config/icon="res://icon.png"
|
||||
config/icon="res://assets/icon.png"
|
||||
|
||||
[autoload]
|
||||
|
||||
Globals="*res://src/Globals.gd"
|
||||
Filesystem="*res://src/Filesystem.gd"
|
||||
ShaderDirectiveParser="*res://src/ShaderDirectiveParser.gd"
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=1280
|
||||
window/size/viewport_height=720
|
||||
window/size/mode=2
|
||||
window/size/viewport_width=704
|
||||
window/size/viewport_height=704
|
||||
window/energy_saving/keep_screen_on=false
|
||||
window/subwindows/embed_subwindows=false
|
||||
window/vsync/vsync_mode=0
|
||||
|
||||
[editor_plugins]
|
||||
|
|
205
scenes/main.tscn
|
@ -1,23 +1,24 @@
|
|||
[gd_scene load_steps=10 format=3 uid="uid://bjah7k4bxo044"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/Main.gd" id="1_2625y"]
|
||||
[ext_resource type="Script" path="res://src/ImageViewport.gd" id="2_hvo65"]
|
||||
[ext_resource type="Script" path="res://src/Compositor.gd" id="2_hvo65"]
|
||||
[ext_resource type="Shader" path="res://src/shader/ivd_outline.gdshader" id="3_6xihe"]
|
||||
[ext_resource type="Script" path="res://src/ImageViewportDisplay.gd" id="3_n4itb"]
|
||||
[ext_resource type="Shader" path="res://src/ui_background.gdshader" id="4_ty3qx"]
|
||||
[ext_resource type="Script" path="res://src/UIAppVersion.gd" id="5_o1ggv"]
|
||||
[ext_resource type="Script" path="res://src/Editor.gd" id="7_g8bap"]
|
||||
[ext_resource type="Script" path="res://src/UIWindow.gd" id="6_8k0ha"]
|
||||
[ext_resource type="PackedScene" uid="uid://btgits2mfup0h" path="res://scenes/ui_container.tscn" id="7_5ci0e"]
|
||||
[ext_resource type="Script" path="res://src/Camera.gd" id="8_mls06"]
|
||||
|
||||
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
|
||||
viewport_path = NodePath("ImageViewport")
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_y2ea0"]
|
||||
shader = ExtResource("3_6xihe")
|
||||
shader_parameter/zoom_level = Vector2(1, 1)
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_onhxk"]
|
||||
shader = ExtResource("4_ty3qx")
|
||||
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
|
||||
viewport_path = NodePath("Compositor")
|
||||
|
||||
[node name="Main" type="Node2D"]
|
||||
script = ExtResource("1_2625y")
|
||||
|
||||
[node name="ImageViewport" type="SubViewport" parent="."]
|
||||
[node name="Compositor" type="SubViewport" parent="."]
|
||||
unique_name_in_owner = true
|
||||
disable_3d = true
|
||||
transparent_bg = true
|
||||
|
@ -25,187 +26,27 @@ canvas_item_default_texture_filter = 0
|
|||
render_target_update_mode = 4
|
||||
script = ExtResource("2_hvo65")
|
||||
|
||||
[node name="ImageSprite" type="Sprite2D" parent="ImageViewport"]
|
||||
[node name="ImageSprite" type="Sprite2D" parent="Compositor"]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
material = SubResource("ShaderMaterial_y2ea0")
|
||||
texture = SubResource("ViewportTexture_lct1c")
|
||||
script = ExtResource("3_n4itb")
|
||||
|
||||
[node name="UI_Layer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="FokusStealer" type="Control" parent="UI_Layer"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
focus_mode = 2
|
||||
|
||||
[node name="UserInterfaceContainer" type="Control" parent="UI_Layer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 3
|
||||
anchor_right = 0.225
|
||||
anchor_bottom = 1.0
|
||||
offset_right = 288.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Background" type="ColorRect" parent="UI_Layer/UserInterfaceContainer"]
|
||||
material = SubResource("ShaderMaterial_onhxk")
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
focus_mode = 2
|
||||
color = Color(1, 1, 1, 0)
|
||||
|
||||
[node name="AppName" type="Label" parent="UI_Layer/UserInterfaceContainer"]
|
||||
layout_mode = 0
|
||||
offset_left = 24.0
|
||||
offset_top = 16.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = 48.0
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "Fragmented"
|
||||
vertical_alignment = 2
|
||||
|
||||
[node name="AppVersion" type="Label" parent="UI_Layer/UserInterfaceContainer"]
|
||||
layout_mode = 0
|
||||
offset_left = 152.0
|
||||
offset_top = 17.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = 47.0
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "v0
|
||||
"
|
||||
vertical_alignment = 2
|
||||
script = ExtResource("5_o1ggv")
|
||||
|
||||
[node name="Editor" type="Control" parent="UI_Layer/UserInterfaceContainer"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 24.0
|
||||
offset_top = 64.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = -24.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("7_g8bap")
|
||||
|
||||
[node name="OpenShaderDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
unique_name_in_owner = true
|
||||
title = "Load Shader"
|
||||
size = Vector2i(521, 175)
|
||||
ok_button_text = "Open"
|
||||
mode_overrides_title = false
|
||||
file_mode = 0
|
||||
access = 2
|
||||
filters = PackedStringArray("*.gdshader")
|
||||
use_native_dialog = true
|
||||
|
||||
[node name="SaveShaderDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
unique_name_in_owner = true
|
||||
title = "Save Shader"
|
||||
size = Vector2i(661, 175)
|
||||
ok_button_text = "Save"
|
||||
mode_overrides_title = false
|
||||
access = 2
|
||||
filters = PackedStringArray("*.gdshader")
|
||||
use_native_dialog = true
|
||||
|
||||
[node name="SaveImageDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
unique_name_in_owner = true
|
||||
title = "Export Image"
|
||||
size = Vector2i(661, 175)
|
||||
ok_button_text = "Save"
|
||||
mode_overrides_title = false
|
||||
access = 2
|
||||
filters = PackedStringArray("*.png")
|
||||
use_native_dialog = true
|
||||
|
||||
[node name="CodeEdit" type="CodeEdit" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 48.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
placeholder_text = "// Test"
|
||||
wrap_mode = 1
|
||||
minimap_draw = true
|
||||
minimap_width = 40
|
||||
caret_blink = true
|
||||
draw_control_chars = true
|
||||
draw_tabs = true
|
||||
draw_spaces = true
|
||||
line_length_guidelines = Array[int]([80])
|
||||
gutters_draw_line_numbers = true
|
||||
code_completion_enabled = true
|
||||
indent_automatic = true
|
||||
auto_brace_completion_enabled = true
|
||||
auto_brace_completion_highlight_matching = true
|
||||
|
||||
[node name="OpenShaderButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
layout_mode = 1
|
||||
offset_right = 56.0
|
||||
offset_bottom = 32.0
|
||||
text = "Open"
|
||||
|
||||
[node name="SaveShaderButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
layout_mode = 1
|
||||
offset_left = 64.0
|
||||
offset_right = 120.0
|
||||
offset_bottom = 32.0
|
||||
text = "Save"
|
||||
|
||||
[node name="ApplyShaderButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
layout_mode = 1
|
||||
offset_left = 128.0
|
||||
offset_right = 216.0
|
||||
offset_bottom = 32.0
|
||||
text = "Apply (F5)"
|
||||
|
||||
[node name="SaveImageButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -120.0
|
||||
offset_bottom = 32.0
|
||||
grow_horizontal = 0
|
||||
text = "Export Image"
|
||||
|
||||
[node name="FitImageButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -216.0
|
||||
offset_right = -128.0
|
||||
offset_bottom = 32.0
|
||||
grow_horizontal = 0
|
||||
text = "Fit Image"
|
||||
|
||||
[node name="Camera" type="Camera2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("8_mls06")
|
||||
|
||||
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/Editor/OpenShaderDialog" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_open_shader_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/Editor/SaveShaderDialog" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_save_shader_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/Editor/SaveImageDialog" to="." method="_on_save_image_dialog_file_selected"]
|
||||
[connection signal="code_completion_requested" from="UI_Layer/UserInterfaceContainer/Editor/CodeEdit" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_code_edit_code_completion_requested"]
|
||||
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/OpenShaderButton" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_open_shader_button_pressed"]
|
||||
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/SaveShaderButton" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_save_shader_button_pressed"]
|
||||
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/ApplyShaderButton" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_apply_shader_button_pressed"]
|
||||
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/SaveImageButton" to="." method="_on_save_image_button_pressed"]
|
||||
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/FitImageButton" to="Camera" method="_on_fit_image_button_pressed"]
|
||||
[node name="EditorWindow" type="Window" parent="."]
|
||||
unique_name_in_owner = true
|
||||
disable_3d = true
|
||||
position = Vector2i(48, 80)
|
||||
size = Vector2i(704, 704)
|
||||
script = ExtResource("6_8k0ha")
|
||||
|
||||
[node name="UserInterfaceContainer" parent="EditorWindow" instance=ExtResource("7_5ci0e")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[connection signal="close_requested" from="EditorWindow" to="EditorWindow" method="_on_close_requested"]
|
||||
|
|
202
scenes/ui_container.tscn
Normal file
|
@ -0,0 +1,202 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://btgits2mfup0h"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/UIAppVersion.gd" id="1_5qvnb"]
|
||||
[ext_resource type="Script" path="res://src/Editor.gd" id="2_haub5"]
|
||||
|
||||
[node name="UserInterfaceContainer" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="AppName" type="Label" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 24.0
|
||||
offset_top = 16.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = 48.0
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "Fragmented"
|
||||
vertical_alignment = 2
|
||||
|
||||
[node name="AppVersion" type="Label" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 152.0
|
||||
offset_top = 17.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = 47.0
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "v0
|
||||
"
|
||||
vertical_alignment = 2
|
||||
script = ExtResource("1_5qvnb")
|
||||
|
||||
[node name="Editor" type="Control" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 24.0
|
||||
offset_top = 64.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = -16.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("2_haub5")
|
||||
|
||||
[node name="OpenShaderDialog" type="FileDialog" parent="Editor"]
|
||||
unique_name_in_owner = true
|
||||
auto_translate_mode = 1
|
||||
title = "Load Shader"
|
||||
size = Vector2i(521, 175)
|
||||
ok_button_text = "Open"
|
||||
mode_overrides_title = false
|
||||
file_mode = 0
|
||||
access = 2
|
||||
filters = PackedStringArray("*.gdshader")
|
||||
use_native_dialog = true
|
||||
|
||||
[node name="SaveShaderDialog" type="FileDialog" parent="Editor"]
|
||||
unique_name_in_owner = true
|
||||
auto_translate_mode = 1
|
||||
title = "Save Shader"
|
||||
size = Vector2i(661, 175)
|
||||
ok_button_text = "Save"
|
||||
mode_overrides_title = false
|
||||
access = 2
|
||||
filters = PackedStringArray("*.gdshader")
|
||||
use_native_dialog = true
|
||||
|
||||
[node name="SaveImageDialog" type="FileDialog" parent="Editor"]
|
||||
unique_name_in_owner = true
|
||||
auto_translate_mode = 1
|
||||
title = "Export Image"
|
||||
size = Vector2i(661, 175)
|
||||
ok_button_text = "Save"
|
||||
mode_overrides_title = false
|
||||
access = 2
|
||||
filters = PackedStringArray("*.png")
|
||||
use_native_dialog = true
|
||||
|
||||
[node name="CodeEdit" type="CodeEdit" parent="Editor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 48.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
placeholder_text = "// Test"
|
||||
wrap_mode = 1
|
||||
minimap_draw = true
|
||||
minimap_width = 40
|
||||
caret_blink = true
|
||||
draw_control_chars = true
|
||||
draw_tabs = true
|
||||
draw_spaces = true
|
||||
line_length_guidelines = Array[int]([80])
|
||||
gutters_draw_line_numbers = true
|
||||
code_completion_enabled = true
|
||||
indent_automatic = true
|
||||
auto_brace_completion_enabled = true
|
||||
auto_brace_completion_highlight_matching = true
|
||||
|
||||
[node name="NewShaderButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
offset_right = 48.0
|
||||
offset_bottom = 32.0
|
||||
text = "New"
|
||||
|
||||
[node name="OpenShaderButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
offset_left = 56.0
|
||||
offset_right = 112.0
|
||||
offset_bottom = 32.0
|
||||
text = "Open"
|
||||
|
||||
[node name="SaveShaderButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
offset_left = 120.0
|
||||
offset_top = -1.0
|
||||
offset_right = 176.0
|
||||
offset_bottom = 31.0
|
||||
text = "Save"
|
||||
|
||||
[node name="SaveShaderAsButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
offset_left = 184.0
|
||||
offset_right = 263.0
|
||||
offset_bottom = 32.0
|
||||
text = "Save As"
|
||||
|
||||
[node name="SaveImageButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -72.0
|
||||
offset_bottom = 32.0
|
||||
grow_horizontal = 0
|
||||
text = "Export"
|
||||
|
||||
[node name="FitImageButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -128.0
|
||||
offset_right = -80.0
|
||||
offset_bottom = 32.0
|
||||
grow_horizontal = 0
|
||||
text = "Fit"
|
||||
|
||||
[node name="ApplyShaderButton" type="Button" parent="Editor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -232.0
|
||||
offset_right = -136.0
|
||||
offset_bottom = 31.0
|
||||
grow_horizontal = 0
|
||||
text = "Apply (F5)"
|
||||
|
||||
[node name="StatusIndicator" type="TextureButton" parent="Editor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -76.0
|
||||
offset_top = 56.0
|
||||
offset_right = -56.0
|
||||
offset_bottom = 76.0
|
||||
grow_horizontal = 0
|
||||
disabled = true
|
||||
ignore_texture_size = true
|
||||
stretch_mode = 0
|
||||
|
||||
[node name="ErrorMessageDialog" type="AcceptDialog" parent="Editor"]
|
||||
unique_name_in_owner = true
|
||||
title = "Status"
|
||||
initial_position = 2
|
||||
size = Vector2i(256, 128)
|
||||
popup_window = true
|
||||
ok_button_text = "Close"
|
||||
|
||||
[connection signal="file_selected" from="Editor/OpenShaderDialog" to="Editor" method="_on_open_shader_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="Editor/SaveShaderDialog" to="Editor" method="_on_save_shader_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="Editor/SaveImageDialog" to="Editor" method="_on_save_image_dialog_file_selected"]
|
||||
[connection signal="code_completion_requested" from="Editor/CodeEdit" to="Editor" method="_on_code_edit_code_completion_requested"]
|
||||
[connection signal="pressed" from="Editor/NewShaderButton" to="Editor" method="_on_new_shader_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/OpenShaderButton" to="Editor" method="_on_open_shader_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/SaveShaderButton" to="Editor" method="_on_save_shader_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/SaveShaderAsButton" to="Editor" method="_on_save_shader_as_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/SaveImageButton" to="Editor" method="_on_save_image_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/FitImageButton" to="Editor" method="_on_fit_image_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/ApplyShaderButton" to="Editor" method="_on_apply_shader_button_pressed"]
|
||||
[connection signal="pressed" from="Editor/StatusIndicator" to="Editor" method="_on_status_indicator_pressed"]
|
BIN
screenshot.png
Before Width: | Height: | Size: 669 KiB After Width: | Height: | Size: 635 KiB |
50
shaderlib/hsv.gdshaderinc
Normal file
|
@ -0,0 +1,50 @@
|
|||
|
||||
// rgb2hsv and hsv2rgb functions adapted
|
||||
// from https://godotshaders.com/shader/hsv-adjustment/
|
||||
// original code by https://godotshaders.com/author/al1-ce/
|
||||
|
||||
// Convert RGB to HSV (hue, saturation, brightness)
|
||||
vec4 rgb2hsv(vec4 c) {
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec4(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x, c.a);
|
||||
}
|
||||
|
||||
// Convert HSV back to RGB (red, green, blue)
|
||||
vec4 hsv2rgb(vec4 c) {
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
vec3 rgb = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
return vec4(rgb.r, rgb.g, rgb.b, c.a);
|
||||
}
|
||||
|
||||
// Offset the hue, saturation and brightness of a RGB color
|
||||
vec4 hsv_offset(
|
||||
vec4 rgba,
|
||||
float offset_hue,
|
||||
float offset_saturation,
|
||||
float offset_brightness
|
||||
) {
|
||||
vec4 c = rgb2hsv(rgba);
|
||||
c.x += offset_hue;
|
||||
c.y += offset_saturation;
|
||||
c.z += offset_brightness;
|
||||
return hsv2rgb(c);
|
||||
}
|
||||
|
||||
// Multiply the hue, saturation and brightness of a RGB color
|
||||
vec4 hsv_multiply(
|
||||
vec4 rgba,
|
||||
float mult_hue,
|
||||
float mult_saturation,
|
||||
float mult_brightness
|
||||
) {
|
||||
vec4 c = rgb2hsv(rgba);
|
||||
c.x *= mult_hue;
|
||||
c.y *= mult_saturation;
|
||||
c.z *= mult_brightness;
|
||||
return hsv2rgb(c);
|
||||
}
|
19
shaderlib/transform.gdshaderinc
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
// Load in a texture from a sampler2D with an offset and scale
|
||||
// See examples/place_texture.gdshader
|
||||
vec4 place_texture(sampler2D sampler, vec2 uv, vec2 texture_pixel_size, vec2 offset, vec2 scale) {
|
||||
vec2 texture_size = vec2(textureSize(sampler, 0));
|
||||
// position of current pixel; sample color c
|
||||
vec2 pos = (uv - offset) / (texture_size*texture_pixel_size) / scale;
|
||||
vec4 c = texture(sampler, pos);
|
||||
// top-left bounds
|
||||
vec2 a = offset;
|
||||
// bottom-right bounds
|
||||
vec2 b = offset + (texture_size*texture_pixel_size) * scale;
|
||||
// check bounds
|
||||
if (
|
||||
a.x < uv.x && a.y < uv.y
|
||||
&& b.x > uv.x && b.y > uv.y
|
||||
) { return c; } // within bounds -> return color
|
||||
return vec4(0); // not within bounds -> return transparency
|
||||
}
|
8
shaderlib/transparency.gdshaderinc
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
// Alpha Blending a over b after Bruce A. Wallace
|
||||
// source: https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
vec4 alpha_blend(vec4 b, vec4 a) {
|
||||
float alpha = a.a + (b.a * (1.0 - a.a));
|
||||
vec3 col = ((a.rgb*a.a) + ((b.rgb*b.a) * (1.0 - a.a)) / alpha);
|
||||
return vec4(col.r, col.g, col.b, alpha);
|
||||
}
|
|
@ -2,44 +2,45 @@ extends Camera2D
|
|||
|
||||
var drag = false
|
||||
|
||||
@onready var user_interface_container = %UserInterfaceContainer
|
||||
@onready var image_viewport = %ImageViewport
|
||||
@onready var image_viewport_display = %ImageViewportDisplay
|
||||
|
||||
func _input(event):
|
||||
if event.is_action_pressed("zoom_out") && !Globals.camera_freeze:
|
||||
if event.is_action_pressed("zoom_out"):
|
||||
zoom_out()
|
||||
elif event.is_action_pressed("zoom_in") && !Globals.camera_freeze:
|
||||
elif event.is_action_pressed("zoom_in"):
|
||||
zoom_in()
|
||||
if event.is_action_pressed("drag") && !Globals.camera_freeze:
|
||||
drag = true
|
||||
if event.is_action_pressed("drag"):
|
||||
self.drag = true
|
||||
elif event.is_action_released("drag"):
|
||||
drag = false
|
||||
if drag && event is InputEventMouseMotion:
|
||||
global_position -= event.relative / zoom
|
||||
self.drag = false
|
||||
if self.drag && event is InputEventMouseMotion:
|
||||
self.global_position -= event.relative / self.zoom
|
||||
|
||||
func fit_image():
|
||||
if image_viewport.image_original_tex != null:
|
||||
var ui_container_size = user_interface_container.size
|
||||
var image_size = image_viewport.image_original_tex.get_size()
|
||||
if Filesystem.original_image != null:
|
||||
var image_size = Filesystem.original_image.get_size()
|
||||
var viewport_size = get_viewport_rect().size
|
||||
var zoomf = (viewport_size.x - ui_container_size.x) / image_size.x / 1.1
|
||||
var zoomf = viewport_size.x / image_size.x / 1.1
|
||||
if zoomf * image_size.y > viewport_size.y:
|
||||
zoomf = viewport_size.y / image_size.y / 1.1
|
||||
zoom = Vector2(zoomf, zoomf)
|
||||
global_position = Vector2(-((ui_container_size.x) / 2 / zoom.x), 0)
|
||||
self.zoom = Vector2(zoomf, zoomf)
|
||||
self.global_position = Vector2(0, 0)
|
||||
|
||||
func update_vd_zoomlevel():
|
||||
image_viewport_display.update_zoom_texture_filter(self.zoom)
|
||||
image_viewport_display.material.set_shader_parameter("zoom_level", self.zoom)
|
||||
|
||||
func zoom_in():
|
||||
var old_mouse_pos = get_global_mouse_position()
|
||||
zoom *= 1.2
|
||||
global_position += old_mouse_pos - get_global_mouse_position()
|
||||
image_viewport_display.update_zoom_texture_filter(zoom)
|
||||
self.zoom *= 1.2
|
||||
self.global_position += old_mouse_pos - get_global_mouse_position()
|
||||
update_vd_zoomlevel()
|
||||
|
||||
func zoom_out():
|
||||
var old_mouse_pos = get_global_mouse_position()
|
||||
zoom *= 1/1.2
|
||||
global_position += old_mouse_pos - get_global_mouse_position()
|
||||
image_viewport_display.update_zoom_texture_filter(zoom)
|
||||
self.zoom *= 1/1.2
|
||||
self.global_position += old_mouse_pos - get_global_mouse_position()
|
||||
update_vd_zoomlevel()
|
||||
|
||||
func _on_fit_image_button_pressed():
|
||||
fit_image()
|
||||
|
|
82
src/Compositor.gd
Normal file
|
@ -0,0 +1,82 @@
|
|||
extends SubViewport
|
||||
|
||||
@onready var camera = %Camera
|
||||
@onready var image_sprite = %ImageSprite
|
||||
@onready var image_viewport_display = %ImageViewportDisplay
|
||||
|
||||
var _fragment_function_regex: RegEx
|
||||
|
||||
func _init():
|
||||
_fragment_function_regex = RegEx.new()
|
||||
_fragment_function_regex.compile(r'\s*void\s+fragment\s*\(\s*\)\s*{\s*')
|
||||
|
||||
func validate_shader_compilation() -> bool:
|
||||
# Inject code to validate shader compilation
|
||||
var shader: Shader = Filesystem.shader.duplicate()
|
||||
var shader_code = shader.code;
|
||||
# -> get position of fragment shader
|
||||
var fragment_function_match = _fragment_function_regex.search(shader.code)
|
||||
if fragment_function_match == null:
|
||||
return false
|
||||
# -> inject uniform
|
||||
var uniform_name = "shader_compilation_validate_" + str(randi_range(999999999, 100000000))
|
||||
var uniform_code_line = "\nuniform bool " + uniform_name + ";\n"
|
||||
shader_code = shader_code.insert(fragment_function_match.get_start(), uniform_code_line)
|
||||
# -> inject variable access to prevent that the uniform gets optimized away
|
||||
shader_code = shader_code.insert(fragment_function_match.get_end() + len(uniform_code_line), "\n" + uniform_name + ";\n")
|
||||
# apply shader code
|
||||
shader.code = shader_code
|
||||
# test if uniform list is empty -> if it is empty, the shader compilation failed
|
||||
return len(shader.get_shader_uniform_list()) > 0
|
||||
|
||||
func update() -> Array: # returns error messages (strings)
|
||||
if not validate_shader_compilation():
|
||||
return ["Shader compilation failed!"]
|
||||
var errors = []
|
||||
var fit_image = false
|
||||
# load texture(s)
|
||||
Filesystem.clear_additional_images()
|
||||
# ... from //!load directive -> TEXTURE
|
||||
var m = ShaderDirectiveParser.parse_load_directive(Filesystem.shader)
|
||||
if len(m) < 1:
|
||||
errors.append("Didn't find a load directive!")
|
||||
return errors
|
||||
var original_image_path = Filesystem.get_absolute_path(m[1])
|
||||
if original_image_path != Filesystem.last_original_image_path:
|
||||
fit_image = true
|
||||
var err = Filesystem.load_original_image(original_image_path)
|
||||
if err != "":
|
||||
errors.append(err)
|
||||
image_viewport_display.hide()
|
||||
return errors
|
||||
# ... from //!load+ directives
|
||||
for n in ShaderDirectiveParser.parse_load_additional_directive(Filesystem.shader):
|
||||
err = Filesystem.load_additional_image(n[1], Filesystem.get_absolute_path(n[2]))
|
||||
if err != "":
|
||||
errors.append(err)
|
||||
if len(errors) > 0:
|
||||
return errors
|
||||
# apply textures
|
||||
image_sprite.texture = Filesystem.original_image
|
||||
image_sprite.offset = Filesystem.original_image.get_size() / 2
|
||||
self.size = Filesystem.original_image.get_size()
|
||||
var mat = ShaderMaterial.new()
|
||||
mat.shader = Filesystem.shader
|
||||
# ... as shader parameters
|
||||
for key in Filesystem.additional_images:
|
||||
mat.set_shader_parameter(
|
||||
key, # uniform param name
|
||||
Filesystem.additional_images[key])
|
||||
# assign material
|
||||
image_sprite.material = mat
|
||||
# Get viewport texture
|
||||
await RenderingServer.frame_post_draw # for good measure
|
||||
Filesystem.result = get_texture().get_image()
|
||||
image_sprite.material = null
|
||||
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
|
||||
if fit_image:
|
||||
camera.fit_image()
|
||||
camera.update_vd_zoomlevel()
|
||||
image_viewport_display.show()
|
||||
# done
|
||||
return errors
|
156
src/Editor.gd
|
@ -1,8 +1,22 @@
|
|||
extends Control
|
||||
|
||||
@onready var code_editor = %CodeEdit
|
||||
|
||||
@onready var open_shader_dialog = %OpenShaderDialog
|
||||
@onready var save_shader_dialog = %SaveShaderDialog
|
||||
@onready var ui_control_filesave = %SaveImageDialog
|
||||
|
||||
@onready var status_indicator = %StatusIndicator
|
||||
@onready var error_msg_dialog = %ErrorMessageDialog
|
||||
|
||||
@onready var main = get_tree().root.get_node("Main")
|
||||
@onready var compositor = main.get_node("%Compositor")
|
||||
@onready var camera = main.get_node("%Camera")
|
||||
|
||||
#
|
||||
|
||||
var status_okay_texture: CompressedTexture2D = preload("uid://m1omb6g45vst")
|
||||
var status_error_texture: CompressedTexture2D = preload("uid://04iv1gogpuhu")
|
||||
|
||||
# # # # # # # # # # #
|
||||
# GDShader keywords #
|
||||
|
@ -62,7 +76,7 @@ const gdshader_builtin_functions = [
|
|||
"radians", "degrees",
|
||||
"sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh",
|
||||
"asinh", "acosh", "atanh",
|
||||
"pow", "exp", "log", "exp2", "log2", "sqrt", "inversesqrt",
|
||||
"pow", "exp", "exp2", "log", "log2", "sqrt", "inversesqrt",
|
||||
"abs", "sign", "floor", "trunc", "round", "roundEven", "ceil", "fract",
|
||||
"mod", "modf", "min", "max", "clamp",
|
||||
"mix", "step", "smoothstep",
|
||||
|
@ -98,19 +112,26 @@ const gdshader_builtins = [
|
|||
"UV",
|
||||
"COLOR",
|
||||
"POINT_SIZE",
|
||||
"MODEL_MATRIX", "CANVAS_MATRIX", "SCREEN_MATRIX",
|
||||
"INSTANCE_CUSTOM", "INSTANCE_ID",
|
||||
"VERTEX_ID",
|
||||
"AT_LIGHT_PASS",
|
||||
"TEXTURE_PIXEL_SIZE",
|
||||
"CUSTOM0", "CUSTOM1",
|
||||
"SHADOW_VERTEX", "LIGHT_VERTEX",
|
||||
"FRAGCOORD",
|
||||
"NORMAL", "NORMAL_MAP", "NORMAL_MAP_DEPTH",
|
||||
"TEXTURE", "NORMAL_TEXTURE",
|
||||
"SCREEN_UV", "SCREEN_PIXEL_SIZE",
|
||||
"TEXTURE",
|
||||
"POINT_COORD",
|
||||
"SPECULAR_SHININESS"
|
||||
]
|
||||
# shaderlib
|
||||
var shaderlib_regex = {
|
||||
"hsv": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/hsv\.gdshaderinc\"'),
|
||||
"transform": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/transform\.gdshaderinc\"'),
|
||||
"transparency": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/transparency\.gdshaderinc\"')
|
||||
}
|
||||
const shaderlib_functions = {
|
||||
"hsv": ["rgb2hsv", "hsv2rgb", "hsv_offset", "hsv_multiply"],
|
||||
"transform": ["place_texture"],
|
||||
"transparency": ["alpha_blend"],
|
||||
}
|
||||
#
|
||||
# configure Highlighter
|
||||
#
|
||||
|
@ -161,23 +182,23 @@ func _on_code_edit_code_completion_requested():
|
|||
for k in gdshader_builtin_functions + gdshader_sub_functions:
|
||||
code_editor.code_completion_prefixes.append(k)
|
||||
code_editor.add_code_completion_option(CodeEdit.KIND_FUNCTION, k, k+"(", Color.INDIAN_RED)
|
||||
# shaderlib #
|
||||
var shader_code = code_editor.text
|
||||
for key in shaderlib_regex:
|
||||
if shaderlib_regex[key].search(shader_code) != null:
|
||||
if key in shaderlib_functions:
|
||||
for k in shaderlib_functions[key]:
|
||||
code_editor.code_completion_prefixes.append(k)
|
||||
code_editor.add_code_completion_option(CodeEdit.KIND_FUNCTION, k, k+"(", Color.INDIAN_RED)
|
||||
# # # # # # #
|
||||
code_editor.update_code_completion_options(true)
|
||||
#
|
||||
# # # # # # # # # # # #
|
||||
|
||||
func _camera_freeze():
|
||||
Globals.camera_freeze = true
|
||||
|
||||
func _camera_unfreeze():
|
||||
Globals.camera_freeze = false
|
||||
|
||||
func _ready():
|
||||
code_editor.code_completion_enabled = true
|
||||
code_editor.syntax_highlighter = ShaderSyntaxHighlighter.new()
|
||||
for c in get_children():
|
||||
c.connect("mouse_entered", _camera_freeze)
|
||||
c.connect("mouse_exited", _camera_unfreeze)
|
||||
update()
|
||||
self.update_code_edit()
|
||||
|
||||
func _input(event):
|
||||
if event.is_action_pressed("apply_shader"):
|
||||
|
@ -186,43 +207,84 @@ func _input(event):
|
|||
accept_event() # Event is now handled.
|
||||
_on_save_shader_button_pressed()
|
||||
|
||||
func update():
|
||||
code_editor.text = Globals.shader.code
|
||||
func update_code_edit():
|
||||
code_editor.text = Filesystem.shader.code
|
||||
|
||||
enum Status {OKAY, ERROR, UNKNOWN = -1}
|
||||
|
||||
func update_status(status: Status, msg: String = ""):
|
||||
error_msg_dialog.dialog_text = msg
|
||||
error_msg_dialog.reset_size()
|
||||
if status == Status.OKAY:
|
||||
status_indicator.texture_normal = status_okay_texture
|
||||
elif status == Status.ERROR:
|
||||
status_indicator.texture_normal = status_error_texture
|
||||
else:
|
||||
status_indicator.texture_normal = null
|
||||
if msg == "":
|
||||
status_indicator.disabled = true
|
||||
else:
|
||||
status_indicator.disabled = false
|
||||
|
||||
#
|
||||
|
||||
func _on_new_shader_button_pressed():
|
||||
main.update_title()
|
||||
Filesystem.reset()
|
||||
self.update_code_edit()
|
||||
compositor.update()
|
||||
update_status(Status.UNKNOWN)
|
||||
|
||||
func _on_open_shader_button_pressed():
|
||||
open_shader_dialog.show()
|
||||
|
||||
func _on_save_shader_button_pressed():
|
||||
if Globals.last_shader_savepath == "":
|
||||
save_shader_dialog.current_file = "shader.gdshader"
|
||||
Filesystem.shader.code = code_editor.text
|
||||
if Filesystem.last_shader_savepath == "":
|
||||
_on_save_shader_as_button_pressed()
|
||||
else:
|
||||
save_shader_dialog.current_path = Globals.last_shader_savepath
|
||||
_on_save_shader_dialog_file_selected(Filesystem.last_shader_savepath)
|
||||
|
||||
func _on_save_shader_as_button_pressed() -> void:
|
||||
Filesystem.shader.code = code_editor.text
|
||||
if Filesystem.last_shader_savepath == "":
|
||||
save_shader_dialog.current_file = "filter.gdshader"
|
||||
else:
|
||||
save_shader_dialog.current_path = Filesystem.last_shader_savepath
|
||||
save_shader_dialog.show()
|
||||
|
||||
func _on_open_shader_dialog_file_selected(path: String):
|
||||
print("Load ", path)
|
||||
var file = FileAccess.open(path, FileAccess.READ)
|
||||
var shader_code = file.get_as_text()
|
||||
var shader = Shader.new()
|
||||
shader.code = shader_code
|
||||
Globals.shader = shader
|
||||
if "/" in path: # update current working directory
|
||||
Globals.cwd = path.substr(0, path.rfind("/"))
|
||||
Globals.target_viewport.update()
|
||||
update()
|
||||
Globals.last_shader_savepath = path
|
||||
|
||||
func _on_save_shader_dialog_file_selected(path):
|
||||
print("Save ", path)
|
||||
var file = FileAccess.open(path, FileAccess.WRITE)
|
||||
var content = code_editor.text
|
||||
file.store_string(content)
|
||||
if "/" in path: # update current working directory
|
||||
Globals.cwd = path.substr(0, path.rfind("/"))
|
||||
Globals.last_shader_savepath = path
|
||||
func _on_fit_image_button_pressed():
|
||||
camera.fit_image()
|
||||
|
||||
func _on_apply_shader_button_pressed():
|
||||
var shader = Shader.new()
|
||||
shader.code = code_editor.text
|
||||
Globals.shader = shader
|
||||
Globals.target_viewport.update()
|
||||
Filesystem.shader.code = code_editor.text
|
||||
var errors = await compositor.update()
|
||||
if len(errors) > 0:
|
||||
update_status(Status.ERROR, "\n".join(errors))
|
||||
else:
|
||||
update_status(Status.OKAY)
|
||||
|
||||
func _on_save_image_button_pressed():
|
||||
if Filesystem.result != null:
|
||||
ui_control_filesave.current_path = Filesystem.last_image_savepath
|
||||
ui_control_filesave.show()
|
||||
|
||||
#
|
||||
|
||||
func _on_open_shader_dialog_file_selected(path: String):
|
||||
Filesystem.load_shader(path)
|
||||
main.update_title(path.split("/")[-1])
|
||||
self.update_code_edit()
|
||||
self._on_apply_shader_button_pressed()
|
||||
|
||||
func _on_save_shader_dialog_file_selected(path):
|
||||
Filesystem.save_shader(path)
|
||||
main.update_title(path.split("/")[-1])
|
||||
|
||||
func _on_save_image_dialog_file_selected(path):
|
||||
Filesystem.save_result(path)
|
||||
|
||||
#
|
||||
|
||||
func _on_status_indicator_pressed() -> void:
|
||||
error_msg_dialog.show()
|
||||
|
|
77
src/Filesystem.gd
Normal file
|
@ -0,0 +1,77 @@
|
|||
extends Node
|
||||
|
||||
@onready var template_shader: Shader = load("res://src/shader/template.gdshader")
|
||||
@onready var shader: Shader = template_shader.duplicate()
|
||||
|
||||
var original_image: ImageTexture
|
||||
var additional_images: Dictionary
|
||||
var result: Image
|
||||
|
||||
var cwd = "."
|
||||
var last_image_savepath = ""
|
||||
var last_shader_savepath = ""
|
||||
var last_original_image_path = ""
|
||||
|
||||
func reset():
|
||||
self.shader = self.template_shader.duplicate()
|
||||
self.last_image_savepath = ""
|
||||
self.last_shader_savepath = ""
|
||||
self.last_original_image_path = ""
|
||||
self.original_image = null
|
||||
self.result = null
|
||||
|
||||
func get_absolute_path(p: String) -> String:
|
||||
# this only works on Linux!
|
||||
if !p.begins_with("/"):
|
||||
return self.cwd + "/" + p.lstrip("./")
|
||||
return p
|
||||
|
||||
func load_original_image(path: String) -> String: # returns an error message
|
||||
var img = Image.new()
|
||||
var err = img.load(path)
|
||||
if err == OK:
|
||||
original_image = ImageTexture.create_from_image(img)
|
||||
if path != self.last_original_image_path:
|
||||
self.last_original_image_path = path
|
||||
if self.last_image_savepath == "":
|
||||
self.last_image_savepath = path
|
||||
return ""
|
||||
return error_string(err) + " " + path
|
||||
|
||||
func clear_additional_images():
|
||||
additional_images.clear()
|
||||
|
||||
func load_additional_image(key: String, path: String) -> String: # returns Error Message String
|
||||
var img = Image.new()
|
||||
var err = img.load(path)
|
||||
if err == OK:
|
||||
additional_images[key] = ImageTexture.create_from_image(img)
|
||||
return ""
|
||||
else:
|
||||
return error_string(err) + " " + path
|
||||
|
||||
func save_result(path: String):
|
||||
print("Export ", path)
|
||||
var err = self.result.save_png(path)
|
||||
if err != OK:
|
||||
print("An error occured!")
|
||||
else:
|
||||
self.last_image_savepath = path
|
||||
|
||||
func load_shader(path: String):
|
||||
print("Load ", path)
|
||||
var file = FileAccess.open(path, FileAccess.READ)
|
||||
var shader_code = file.get_as_text()
|
||||
self.shader = Shader.new()
|
||||
shader.code = shader_code
|
||||
if "/" in path: # update current working directory
|
||||
self.cwd = path.substr(0, path.rfind("/"))
|
||||
self.last_shader_savepath = path
|
||||
|
||||
func save_shader(path: String):
|
||||
print("Save ", path)
|
||||
var file = FileAccess.open(path, FileAccess.WRITE)
|
||||
file.store_string(self.shader.code)
|
||||
if "/" in path: # update current working directory
|
||||
self.cwd = path.substr(0, path.rfind("/"))
|
||||
self.last_shader_savepath = path
|
|
@ -1,8 +0,0 @@
|
|||
extends Node
|
||||
|
||||
var camera_freeze = false
|
||||
@onready var shader: Shader = load("res://src/shaders/empty.gdshader")
|
||||
var target_viewport: SubViewport
|
||||
var cwd = "."
|
||||
var last_image_savepath = ""
|
||||
var last_shader_savepath = ""
|
|
@ -1,71 +0,0 @@
|
|||
extends SubViewport
|
||||
|
||||
@onready var camera = %Camera
|
||||
@onready var image_sprite = %ImageSprite
|
||||
|
||||
var image_original_tex: ImageTexture
|
||||
var image_result: Image
|
||||
var load_regex: RegEx
|
||||
var load_additional_regex: RegEx
|
||||
var last_tex_path = ""
|
||||
|
||||
func _ready():
|
||||
load_regex = RegEx.new()
|
||||
load_additional_regex = RegEx.new()
|
||||
load_regex.compile(r'\/\/!load\s(.*)')
|
||||
load_additional_regex.compile(r'\/\/!load\+\s(\w*)\s(.*)')
|
||||
|
||||
func load_texture(path):
|
||||
print("Load ", path)
|
||||
var img = Image.new()
|
||||
var err = img.load(path)
|
||||
if err == OK:
|
||||
image_original_tex = ImageTexture.create_from_image(img)
|
||||
image_sprite.texture = image_original_tex
|
||||
image_sprite.offset = image_original_tex.get_size() / 2
|
||||
size = image_original_tex.get_size()
|
||||
else:
|
||||
print("An error occured!")
|
||||
|
||||
func get_absolute_path(p: String) -> String:
|
||||
if !p.begins_with("/"):
|
||||
return Globals.cwd + "/" + p.lstrip("./")
|
||||
return p
|
||||
|
||||
func update():
|
||||
# load images from //!load directive -> TEXTURE
|
||||
var regex_match = load_regex.search(Globals.shader.code)
|
||||
if regex_match == null: # Error!
|
||||
print("Didn't find any load directives!")
|
||||
return
|
||||
var tex_path = get_absolute_path(regex_match.strings[1])
|
||||
load_texture(tex_path) # load every time
|
||||
if tex_path != last_tex_path:
|
||||
camera.fit_image()
|
||||
last_tex_path = tex_path
|
||||
if Globals.last_image_savepath == "":
|
||||
Globals.last_image_savepath = tex_path
|
||||
image_sprite.texture = image_original_tex
|
||||
var mat = ShaderMaterial.new()
|
||||
mat.shader = Globals.shader
|
||||
# load images from //!load+ directives and apply them to
|
||||
# the material as shader parameters
|
||||
for m in load_additional_regex.search_all(Globals.shader.code):
|
||||
# this only works for Linux!
|
||||
var img_path = get_absolute_path(m.strings[2])
|
||||
#
|
||||
print("Load ", img_path)
|
||||
var u_image = Image.load_from_file(img_path)
|
||||
mat.set_shader_parameter(
|
||||
m.strings[1], # uniform param name
|
||||
ImageTexture.create_from_image(u_image))
|
||||
# assign material
|
||||
image_sprite.material = mat
|
||||
# Get viewport texture
|
||||
await RenderingServer.frame_post_draw # for good measure
|
||||
image_result = get_texture().get_image()
|
||||
image_sprite.material = null
|
||||
image_sprite.texture = ImageTexture.create_from_image(image_result)
|
||||
|
||||
func get_result():
|
||||
return image_result
|
|
@ -1,5 +1,8 @@
|
|||
extends Sprite2D
|
||||
|
||||
func _ready() -> void:
|
||||
hide()
|
||||
|
||||
func update_zoom_texture_filter(zoom: Vector2):
|
||||
if zoom.x >= 1.5:
|
||||
texture_filter = TEXTURE_FILTER_NEAREST_WITH_MIPMAPS
|
||||
|
|
33
src/Main.gd
|
@ -1,22 +1,21 @@
|
|||
extends Node2D
|
||||
|
||||
@onready var image_viewport = %ImageViewport
|
||||
@onready var ui_control_filesave = %SaveImageDialog
|
||||
extends Node
|
||||
|
||||
@onready var editor_window = %EditorWindow
|
||||
@onready var app_name = ProjectSettings.get_setting("application/config/name")
|
||||
|
||||
func _ready():
|
||||
DisplayServer.window_set_min_size(Vector2i(900, 500))
|
||||
Globals.target_viewport = image_viewport
|
||||
update_title()
|
||||
# position windows
|
||||
get_window().position = Vector2i(
|
||||
editor_window.position.x + editor_window.size.x + 50,
|
||||
editor_window.position.y)
|
||||
get_window().min_size = Vector2i(400, 400)
|
||||
editor_window.min_size = Vector2i(560, 400)
|
||||
|
||||
func _on_save_image_button_pressed():
|
||||
if image_viewport.get_result() != null:
|
||||
ui_control_filesave.current_path = Globals.last_image_savepath
|
||||
ui_control_filesave.show()
|
||||
|
||||
func _on_save_image_dialog_file_selected(path):
|
||||
print("Export ", path)
|
||||
var err = image_viewport.get_result().save_png(path)
|
||||
if err != OK:
|
||||
print("An error occured!")
|
||||
func update_title(current_file: String = ""):
|
||||
if current_file == "":
|
||||
get_window().title = app_name + " - Viewer"
|
||||
editor_window.title = app_name + " - Editor"
|
||||
else:
|
||||
Globals.last_image_savepath = path
|
||||
get_window().title = current_file + " - " + app_name + " - Viewer"
|
||||
editor_window.title = current_file + " - " + app_name + " - Editor"
|
||||
|
|
22
src/ShaderDirectiveParser.gd
Normal file
|
@ -0,0 +1,22 @@
|
|||
extends Node
|
||||
|
||||
var _load_regex: RegEx
|
||||
var _load_additional_regex: RegEx
|
||||
|
||||
func _ready():
|
||||
self._load_regex = RegEx.new()
|
||||
self._load_additional_regex = RegEx.new()
|
||||
self._load_regex.compile(r'\/\/!load\s(.*)')
|
||||
self._load_additional_regex.compile(r'\/\/!load\+\s(\w*)\s(.*)')
|
||||
|
||||
func parse_load_directive(shader: Shader) -> PackedStringArray:
|
||||
var regex_match = self._load_regex.search(Filesystem.shader.code)
|
||||
if regex_match == null:
|
||||
return []
|
||||
return regex_match.strings
|
||||
|
||||
func parse_load_additional_directive(shader: Shader) -> Array[PackedStringArray]:
|
||||
var results : Array[PackedStringArray] = []
|
||||
for m in self._load_additional_regex.search_all(shader.code):
|
||||
results.append(m.strings)
|
||||
return results
|
4
src/UIWindow.gd
Normal file
|
@ -0,0 +1,4 @@
|
|||
extends Window
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
get_tree().quit()
|
16
src/shader/ivd_outline.gdshader
Normal file
|
@ -0,0 +1,16 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
uniform vec2 zoom_level = vec2(1.0);
|
||||
const float thickness = 3.0;
|
||||
|
||||
void fragment() {
|
||||
vec2 t = thickness * TEXTURE_PIXEL_SIZE / zoom_level;
|
||||
if (
|
||||
UV.x < t.x ||
|
||||
UV.y < t.y ||
|
||||
UV.x > 1.0-t.x ||
|
||||
UV.y > 1.0-t.y
|
||||
) {
|
||||
COLOR = mix(COLOR, vec4(1.0), 0.5);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, repeat_disable, filter_linear_mipmap_anisotropic;
|
||||
|
||||
float rand(vec2 uv) {
|
||||
return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 new_uv;
|
||||
new_uv.x = mix(SCREEN_UV.x, rand(UV), 0.2);
|
||||
new_uv.y = mix(SCREEN_UV.y, rand(UV), 0.2);
|
||||
new_uv.x = mix(new_uv.x, rand(UV), -0.2);
|
||||
new_uv.y = mix(new_uv.y, rand(UV), -0.2);
|
||||
COLOR.rgb = clamp(textureLod(SCREEN_TEXTURE, new_uv, 10.0).rgb, 0.05, 0.7);
|
||||
COLOR.a = 1.0;
|
||||
}
|