Compare commits
No commits in common. "main" and "v8.1" have entirely different histories.
1
.gitignore
vendored
|
@ -16,6 +16,7 @@ mono_crash.*.json
|
||||||
*.x86_64
|
*.x86_64
|
||||||
godot.*.template_release.*
|
godot.*.template_release.*
|
||||||
dist/*
|
dist/*
|
||||||
|
!dist/.gitkeep
|
||||||
|
|
||||||
screenshot.png.import
|
screenshot.png.import
|
||||||
|
|
||||||
|
|
44
README.md
|
@ -21,16 +21,12 @@ You can find the latest releases [here](https://github.com/ChaoticByte/Fragmente
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
With Fragemented, you are processing images with GDShaders. This brings almost endless opportunities to create unique art.
|
With Fragemented, you are editing images by writing GDShaders. This brings almost endless opportunities to create unique art.
|
||||||
If you want to learn GDShader, take a look at the [Godot docs](https://docs.godotengine.org/en/stable/tutorials/shaders/).
|
If you want to learn GDShader, take a look at the [Godot docs](https://docs.godotengine.org/en/stable/tutorials/shaders/).
|
||||||
|
|
||||||
**The builtin editor got removed** from Fragmented with version **v9.0**. I advise you to write your shaders directly in the Godot Editor.
|
The repo also includes examples. You can use them as a starting-point to write your own filters.
|
||||||
|
|
||||||
**To get started, use the project template (see the Releases section of this repo) and open it in Godot.**
|
Besides the regular GDShader stuff, Fragmented also has so-called directives. Those allow to further control the behaviour of the application. The most important directive is `//!load` to load an image.
|
||||||
|
|
||||||
The template includes many examples. You can use them as a starting-point to write your own stuff.
|
|
||||||
|
|
||||||
Besides the regular GDShader stuff, Fragmented has so-called directives. Those allow to further control the behaviour of the application. **The most important directive is `//!load` to load an image.**
|
|
||||||
|
|
||||||
### Load TEXTURE using the `//!load` directive
|
### Load TEXTURE using the `//!load` directive
|
||||||
|
|
||||||
|
@ -62,22 +58,20 @@ Example:
|
||||||
//!load ...
|
//!load ...
|
||||||
//!steps 5
|
//!steps 5
|
||||||
|
|
||||||
uniform int STEP;
|
|
||||||
uniform int STEPS;
|
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
if (STEP == 0) {
|
if (STEP == 0) {
|
||||||
...
|
...
|
||||||
} else if (STEP == 1) {
|
} else if (STEP == 1) {
|
||||||
...
|
...
|
||||||
} else if (STEP == STEPS-1) {
|
|
||||||
...
|
|
||||||
}
|
}
|
||||||
|
// ... and so on
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Shaderlib
|
## Shaderlib
|
||||||
|
|
||||||
|
> Note: The shaderlib API is still unstable as I am figuring things out. It will be declared stable with version 10.
|
||||||
|
|
||||||
This repo comes with a (still small) shader library including pre-written functions and more.
|
This repo comes with a (still small) shader library including pre-written functions and more.
|
||||||
Have a look at the `shaderlib` folder.
|
Have a look at the `shaderlib` folder.
|
||||||
|
|
||||||
|
@ -86,7 +80,7 @@ Here is an example:
|
||||||
```glsl
|
```glsl
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
#include "./shaderlib/oklab.gdshaderinc"
|
#include "res://shaderlib/oklab.gdshaderinc"
|
||||||
|
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
|
|
||||||
|
@ -107,22 +101,7 @@ You can run Fragmented from the commandline or scripts.
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
~ Fragmented CLI ~
|
./Fragmented cmd --shader PATH [--load-image PATH]
|
||||||
-================-
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
./Fragmented <command> <args...>
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
|
|
||||||
help
|
|
||||||
|
|
||||||
| Shows this help text.
|
|
||||||
|
|
||||||
apply --shader PATH [--load-image PATH]
|
|
||||||
|
|
||||||
| Applies a shader file.
|
|
||||||
|
|
||||||
--shader PATH The path to the shader
|
--shader PATH The path to the shader
|
||||||
--output PATH Where to write the resulting image to.
|
--output PATH Where to write the resulting image to.
|
||||||
|
@ -134,6 +113,8 @@ Commands:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also run `./Fragmented cmd help` to show the help message.
|
||||||
|
|
||||||
### Batch Mode
|
### Batch Mode
|
||||||
|
|
||||||
Since version v8.0, you can pass a directory to `--load-image` and `--output`. This will process all images in the input directory and write the output to the output directory.
|
Since version v8.0, you can pass a directory to `--load-image` and `--output`. This will process all images in the input directory and write the output to the output directory.
|
||||||
|
@ -143,14 +124,15 @@ Since version v8.0, you can pass a directory to `--load-image` and `--output`. T
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
./Fragmented apply --shader ./examples/oklab.gdshader --output ./output.png
|
./Fragmented cmd --shader ./examples/oklab.gdshader --output ./output.png
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
./Fragmented apply --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png
|
./Fragmented cmd --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png
|
||||||
```
|
```
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
- screen scaling is unsupported; Using screen scaling could lead to an either blurry UI, or no scaling at all -> see #45
|
- screen scaling is unsupported; Using screen scaling could lead to an either blurry UI, or no scaling at all -> see #45
|
||||||
|
- the shaderlib API is still unstable, this will change with version 10
|
||||||
- commandline interface: `--headless` is not supported
|
- commandline interface: `--headless` is not supported
|
||||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -3,15 +3,15 @@
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://04iv1gogpuhu"
|
uid="uid://04iv1gogpuhu"
|
||||||
path="res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"
|
path="res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://src/assets/error.svg"
|
source_file="res://assets/error.svg"
|
||||||
dest_files=["res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"]
|
dest_files=["res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
|
@ -3,15 +3,15 @@
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://kqwc4avs2xdp"
|
uid="uid://kqwc4avs2xdp"
|
||||||
path="res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"
|
path="res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://src/assets/icon.png"
|
source_file="res://assets/icon.png"
|
||||||
dest_files=["res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"]
|
dest_files=["res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -3,15 +3,15 @@
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://m1omb6g45vst"
|
uid="uid://m1omb6g45vst"
|
||||||
path="res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"
|
path="res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://src/assets/okay.svg"
|
source_file="res://assets/okay.svg"
|
||||||
dest_files=["res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"]
|
dest_files=["res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
|
@ -7,12 +7,13 @@ FROM docker.io/ubuntu:focal AS os-base
|
||||||
# https://docs.godotengine.org/en/stable/contributing/development/compiling/compiling_for_linuxbsd.html
|
# https://docs.godotengine.org/en/stable/contributing/development/compiling/compiling_for_linuxbsd.html
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq python3-pip git build-essential 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 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 pip3 install --system scons
|
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq git
|
||||||
|
|
||||||
FROM os-base AS clone-src
|
FROM os-base AS clone-src
|
||||||
|
|
||||||
RUN git clone https://github.com/godotengine/godot.git -b 4.4-stable /godot-src
|
RUN git clone https://github.com/godotengine/godot.git -b 4.3-stable /godot-src
|
||||||
|
|
||||||
FROM clone-src
|
FROM clone-src
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
function log {
|
function log {
|
||||||
echo -e "\033[1;36m***** $@ *****\033[0m"
|
echo -e "\033[1;36m* $@\033[0m"
|
||||||
}
|
}
|
||||||
|
|
||||||
log " "
|
log
|
||||||
log "Fragmented - Godot Build Template Builder"
|
log "Fragmented - Godot Build Template Builder"
|
||||||
log " "
|
log
|
||||||
|
|
||||||
cd $(dirname $0)
|
cd $(dirname $0)
|
||||||
log Switched to $(pwd)
|
log Switched to $(pwd)
|
||||||
|
|
30
dist.sh
|
@ -1,30 +0,0 @@
|
||||||
set -e
|
|
||||||
|
|
||||||
function log {
|
|
||||||
echo -e "\033[1;36m***** $@ *****\033[0m"
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir -p dist
|
|
||||||
|
|
||||||
log Building application
|
|
||||||
|
|
||||||
VERSION="$(godot --headless --no-header -s tools/get_version.gd)"
|
|
||||||
|
|
||||||
godot --headless --export-release "Linux/X11" "dist/Fragmented-${VERSION}.x86_64"
|
|
||||||
|
|
||||||
log Packing shaderlib
|
|
||||||
|
|
||||||
ZIP_PATH_SHADERLIB=$(realpath "dist/Fragmented-${VERSION}_shaderlib.zip")
|
|
||||||
|
|
||||||
zip -r "${ZIP_PATH_SHADERLIB}" shaderlib/
|
|
||||||
|
|
||||||
log Packing project template
|
|
||||||
|
|
||||||
ZIP_PATH_PROJECT_TEMPLATE=$(realpath "dist/Fragmented-${VERSION}_project_template.zip")
|
|
||||||
|
|
||||||
rm -f "${ZIP_PATH_PROJECT_TEMPLATE}"
|
|
||||||
(
|
|
||||||
cd examples/
|
|
||||||
mv project.godot_ project.godot && trap "mv project.godot project.godot_" EXIT
|
|
||||||
zip -r "${ZIP_PATH_PROJECT_TEMPLATE}" *
|
|
||||||
)
|
|
0
dist/.gitkeep
vendored
Normal file
|
@ -1,3 +0,0 @@
|
||||||
[gd_scene format=3 uid="uid://db2rhq8rwv5wo"]
|
|
||||||
|
|
||||||
[node name="Node" type="Node"]
|
|
|
@ -1,7 +1,7 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
#include "./shaderlib/blur.gdshaderinc"
|
#include "res://shaderlib/blur.gdshaderinc"
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
COLOR = gaussian_blur(TEXTURE, UV, 48, 24.0);
|
COLOR = gaussian_blur(TEXTURE, UV, 48, 24.0);
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://cny8dtukv54wt
|
|
|
@ -1 +0,0 @@
|
||||||
uid://0efk4fornlg6
|
|
|
@ -1,7 +1,7 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
#include "./shaderlib/hsv.gdshaderinc"
|
#include "res://shaderlib/hsv.gdshaderinc"
|
||||||
#include "./shaderlib/pixelate.gdshaderinc"
|
#include "res://shaderlib/pixelate.gdshaderinc"
|
||||||
|
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://gd23hu7ro148
|
|
|
@ -2,7 +2,7 @@ shader_type canvas_item;
|
||||||
|
|
||||||
//!load ./images/noisy.png
|
//!load ./images/noisy.png
|
||||||
|
|
||||||
#include "./shaderlib/denoise.gdshaderinc"
|
#include "res://shaderlib/denoise.gdshaderinc"
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
COLOR = smart_denoise(TEXTURE, UV, 12.0, 1.0, .12);
|
COLOR = smart_denoise(TEXTURE, UV, 12.0, 1.0, .12);
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://cbwyneu03fki6
|
|
|
@ -1 +0,0 @@
|
||||||
uid://dvarqolt6es27
|
|
|
@ -3,4 +3,3 @@
|
||||||
|
|
||||||
- swamp.jpg by [clfr21 on Pixabay](https://pixabay.com/de/users/clfr21-6530007/)
|
- 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/)
|
- grass.png by [GDJ on Pixabay](https://pixabay.com/users/gdj-1086657/)
|
||||||
- mountain.jpg by [Phghvvcftyyufj on Pixabay](https://pixabay.com/users/phghvvcftyyufj-12646982)
|
|
||||||
|
|
Before Width: | Height: | Size: 814 KiB |
|
@ -1,34 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://ben72llmopgaj"
|
|
||||||
path="res://.godot/imported/mountain.jpg-c1b7de1e6557b826bc6f9324027e11af.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://examples/images/mountain.jpg"
|
|
||||||
dest_files=["res://.godot/imported/mountain.jpg-c1b7de1e6557b826bc6f9324027e11af.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
|
|
|
@ -1,16 +0,0 @@
|
||||||
shader_type canvas_item;
|
|
||||||
|
|
||||||
//!load ./images/mountain.jpg
|
|
||||||
|
|
||||||
#include "./shaderlib/kuwahara.gdshaderinc"
|
|
||||||
#include "./shaderlib/hsv.gdshaderinc"
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
// Kuwahara
|
|
||||||
COLOR.rgb = kuwahara(TEXTURE, UV, 20, 80.0, 18.0, 0.6, .15, 8);
|
|
||||||
// A litte bit of color adjustments
|
|
||||||
vec4 hsv = rgb2hsv(COLOR);
|
|
||||||
hsv.x += .03;
|
|
||||||
hsv.y *= 1.4;
|
|
||||||
COLOR = hsv2rgb(hsv);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
uid://cdhyuk7u8kxyk
|
|
|
@ -1 +0,0 @@
|
||||||
uid://dn02xsjm1kok8
|
|
|
@ -3,8 +3,6 @@ shader_type canvas_item;
|
||||||
//!steps 9
|
//!steps 9
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
|
|
||||||
uniform int STEP;
|
|
||||||
|
|
||||||
const float strength = 0.01;
|
const float strength = 0.01;
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://c17u5jx7a7o81
|
|
|
@ -1,20 +0,0 @@
|
||||||
shader_type canvas_item;
|
|
||||||
|
|
||||||
#include "./shaderlib/pixelsort.gdshaderinc"
|
|
||||||
|
|
||||||
//!steps 1500
|
|
||||||
uniform int STEP;
|
|
||||||
|
|
||||||
//!load ./images/mountain.jpg
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
// pixel sorting works in multiple steps
|
|
||||||
COLOR = pixelsort_step(
|
|
||||||
TEXTURE, UV,
|
|
||||||
DIRECTION_BOTTOM_TO_TOP,
|
|
||||||
COLOR_MODE_OKLCH,
|
|
||||||
{true, false, false},
|
|
||||||
{-INF, .007, -INF},
|
|
||||||
{INF, INF, INF},
|
|
||||||
STEP);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
uid://csk0fg4by651b
|
|
|
@ -1,6 +1,6 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
#include "./shaderlib/oklab.gdshaderinc"
|
#include "res://shaderlib/oklab.gdshaderinc"
|
||||||
|
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://cu37y8lc0x83
|
|
|
@ -1,7 +1,7 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
#include "./shaderlib/place_texture.gdshaderinc"
|
#include "res://shaderlib/place_texture.gdshaderinc"
|
||||||
#include "./shaderlib/common.gdshaderinc"
|
#include "res://shaderlib/common.gdshaderinc"
|
||||||
|
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
//!load+ img2 ./images/grass.png
|
//!load+ img2 ./images/grass.png
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://dybe4t5rbbkc6
|
|
|
@ -1,15 +0,0 @@
|
||||||
; Engine configuration file.
|
|
||||||
; It's best edited using the editor UI and not directly,
|
|
||||||
; since the parameters that go here are not all obvious.
|
|
||||||
;
|
|
||||||
; Format:
|
|
||||||
; [section] ; section goes between []
|
|
||||||
; param=value ; assign values to parameters
|
|
||||||
|
|
||||||
config_version=5
|
|
||||||
|
|
||||||
[application]
|
|
||||||
|
|
||||||
config/name="Fragmented Project"
|
|
||||||
run/main_scene="res://0_empty.tscn"
|
|
||||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
|
|
@ -1 +0,0 @@
|
||||||
../shaderlib
|
|
|
@ -1,10 +0,0 @@
|
||||||
shader_type canvas_item;
|
|
||||||
|
|
||||||
//!load ./images/noisy.png
|
|
||||||
|
|
||||||
#include "./shaderlib/sobel.gdshaderinc"
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
// Sobel Filter
|
|
||||||
COLOR = sobel(TEXTURE, UV);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
uid://h376mk1fq4ky
|
|
|
@ -8,12 +8,10 @@ dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter=""
|
include_filter=""
|
||||||
exclude_filter="screenshot.png, examples/*, shaderlib/*, tools/*, build-template/*"
|
exclude_filter="screenshot.png, examples/*"
|
||||||
export_path="dist/Fragmented.x86_64"
|
export_path="dist/Fragmented.x86_64"
|
||||||
patches=PackedStringArray()
|
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
encryption_exclude_filters=""
|
encryption_exclude_filters=""
|
||||||
seed=0
|
|
||||||
encrypt_pck=false
|
encrypt_pck=false
|
||||||
encrypt_directory=false
|
encrypt_directory=false
|
||||||
script_export_mode=2
|
script_export_mode=2
|
||||||
|
|
|
@ -11,10 +11,11 @@ config_version=5
|
||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Fragmented"
|
config/name="Fragmented"
|
||||||
config/version="v10.2"
|
config/version="v8.1"
|
||||||
run/main_scene="res://src/scenes/main.tscn"
|
run/main_scene="res://scenes/main.tscn"
|
||||||
config/features=PackedStringArray("4.4", "Mobile")
|
config/features=PackedStringArray("4.3", "Mobile")
|
||||||
config/icon="res://src/assets/icon.png"
|
run/low_processor_mode=true
|
||||||
|
config/icon="res://assets/icon.png"
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
||||||
|
@ -23,10 +24,11 @@ ShaderDirectiveParser="*res://src/ShaderDirectiveParser.gd"
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
window/size/viewport_width=640
|
window/size/viewport_width=704
|
||||||
window/size/viewport_height=672
|
window/size/viewport_height=704
|
||||||
window/energy_saving/keep_screen_on=false
|
window/energy_saving/keep_screen_on=false
|
||||||
window/subwindows/embed_subwindows=false
|
window/subwindows/embed_subwindows=false
|
||||||
|
window/vsync/vsync_mode=0
|
||||||
|
|
||||||
[editor_plugins]
|
[editor_plugins]
|
||||||
|
|
||||||
|
@ -70,4 +72,4 @@ renderer/rendering_method="mobile"
|
||||||
textures/vram_compression/import_etc2_astc=true
|
textures/vram_compression/import_etc2_astc=true
|
||||||
textures/lossless_compression/force_png=true
|
textures/lossless_compression/force_png=true
|
||||||
shader_compiler/shader_cache/enabled=false
|
shader_compiler/shader_cache/enabled=false
|
||||||
environment/defaults/default_clear_color=Color(0.501961, 0.501961, 0.501961, 1)
|
environment/defaults/default_clear_color=Color(0, 0, 0, 1)
|
||||||
|
|
46
scenes/main.tscn
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
[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/ImageCompositor.gd" id="2_4thch"]
|
||||||
|
[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="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="ShaderMaterial" id="ShaderMaterial_y2ea0"]
|
||||||
|
shader = ExtResource("3_6xihe")
|
||||||
|
shader_parameter/zoom_level = Vector2(1, 1)
|
||||||
|
|
||||||
|
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
|
||||||
|
viewport_path = NodePath("Compositor")
|
||||||
|
|
||||||
|
[node name="Main" type="Node2D"]
|
||||||
|
script = ExtResource("1_2625y")
|
||||||
|
|
||||||
|
[node name="Compositor" type="SubViewport" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("2_4thch")
|
||||||
|
|
||||||
|
[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="Camera" type="Camera2D" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("8_mls06")
|
||||||
|
|
||||||
|
[node name="EditorWindow" type="Window" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
disable_3d = true
|
||||||
|
position = Vector2i(48, 36)
|
||||||
|
size = Vector2i(704, 704)
|
||||||
|
visible = false
|
||||||
|
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"]
|
204
scenes/ui_container.tscn
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
[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"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
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
|
||||||
|
disabled = true
|
||||||
|
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: 502 KiB After Width: | Height: | Size: 630 KiB |
|
@ -1 +0,0 @@
|
||||||
uid://bjtljvcjcu6dr
|
|
|
@ -13,15 +13,3 @@ vec4 alpha_blend(vec4 b, vec4 a) {
|
||||||
vec3 col = ((a.rgb*a.a) + ((b.rgb*b.a) * (1.0 - a.a)) / alpha);
|
vec3 col = ((a.rgb*a.a) + ((b.rgb*b.a) * (1.0 - a.a)) / alpha);
|
||||||
return vec4(col.r, col.g, col.b, alpha);
|
return vec4(col.r, col.g, col.b, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Rotate UV
|
|
||||||
*/
|
|
||||||
|
|
||||||
vec2 rotateUV(vec2 uv, float rotation, vec2 center) {
|
|
||||||
float cosRot = cos(rotation);
|
|
||||||
float sinRot = sin(rotation);
|
|
||||||
return vec2(
|
|
||||||
cosRot * (uv.x - center.x) + sinRot * (uv.y - center.y) + center.x,
|
|
||||||
cosRot * (uv.y - center.y) - sinRot * (uv.x - center.x) + center.y);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://764b6ekchgb8
|
|
|
@ -1 +0,0 @@
|
||||||
uid://b7ksfifyyfcip
|
|
|
@ -1 +0,0 @@
|
||||||
uid://bbr3tq6mp5qa2
|
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
Kuwahara Filter, adapted
|
|
||||||
original code: https://godotshaders.com/shader/generalized-kuwahara/
|
|
||||||
original authors:
|
|
||||||
- https://godotshaders.com/author/firerabbit/
|
|
||||||
- https://github.com/GarrettGunnell (Acerola)
|
|
||||||
license of the original code:
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 Garrett Gunnell
|
|
||||||
Copyright (c) 2024 Firerabbit
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies
|
|
||||||
or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
vec3 kuwahara(
|
|
||||||
sampler2D texture,
|
|
||||||
vec2 uv,
|
|
||||||
int kernel_size, // should be > 2 - high values will affect performance
|
|
||||||
float hardness, // should be in the range of 1.0 - 100.0
|
|
||||||
float sharpness, // should be in the range of 1.0 - 18.0
|
|
||||||
float zero_crossing, // should be in the range of 0.5 - 2.0
|
|
||||||
float zeta, // should be in the range of 0.01 - 3.0
|
|
||||||
int n // number of iterations, should be 8, must be <= 8
|
|
||||||
) {
|
|
||||||
vec2 texelSize = vec2(1.0 / vec2(textureSize(texture, 0)));
|
|
||||||
vec4 m[8];
|
|
||||||
vec3 s[8];
|
|
||||||
|
|
||||||
int kernel_radius = kernel_size / 2;
|
|
||||||
|
|
||||||
float sin_zero_crossing = sin(zero_crossing);
|
|
||||||
float eta = (zeta + cos(zero_crossing)) / (sin_zero_crossing * sin_zero_crossing);
|
|
||||||
|
|
||||||
for (int k = 0; k < n; ++k) {
|
|
||||||
m[k] = vec4(0.0f);
|
|
||||||
s[k] = vec3(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = -kernel_radius; y <= kernel_radius; ++y) {
|
|
||||||
for (int x = -kernel_radius; x <= kernel_radius; ++x) {
|
|
||||||
vec2 v = vec2(float(x), float(y)) / float(kernel_radius);
|
|
||||||
vec3 c = texture(texture, uv + vec2(float(x), float(y)) * texelSize.xy).rgb;
|
|
||||||
c = clamp(c, 0.0f, 1.0f);
|
|
||||||
float sum = 0.0f;
|
|
||||||
float w[8];
|
|
||||||
float z, vxx, vyy;
|
|
||||||
|
|
||||||
/* Calculate Polynomial Weights */
|
|
||||||
vxx = zeta - eta * v.x * v.x;
|
|
||||||
vyy = zeta - eta * v.y * v.y;
|
|
||||||
z = max(0, v.y + vxx);
|
|
||||||
w[0] = z * z;
|
|
||||||
sum += w[0];
|
|
||||||
z = max(0, -v.x + vyy);
|
|
||||||
w[2] = z * z;
|
|
||||||
sum += w[2];
|
|
||||||
z = max(0, -v.y + vxx);
|
|
||||||
w[4] = z * z;
|
|
||||||
sum += w[4];
|
|
||||||
z = max(0, v.x + vyy);
|
|
||||||
w[6] = z * z;
|
|
||||||
sum += w[6];
|
|
||||||
v = sqrt(2.0f) / 2.0f * vec2(v.x - v.y, v.x + v.y);
|
|
||||||
vxx = zeta - eta * v.x * v.x;
|
|
||||||
vyy = zeta - eta * v.y * v.y;
|
|
||||||
z = max(0, v.y + vxx);
|
|
||||||
w[1] = z * z;
|
|
||||||
sum += w[1];
|
|
||||||
z = max(0, -v.x + vyy);
|
|
||||||
w[3] = z * z;
|
|
||||||
sum += w[3];
|
|
||||||
z = max(0, -v.y + vxx);
|
|
||||||
w[5] = z * z;
|
|
||||||
sum += w[5];
|
|
||||||
z = max(0, v.x + vyy);
|
|
||||||
w[7] = z * z;
|
|
||||||
sum += w[7];
|
|
||||||
|
|
||||||
float g = exp(-3.125f * dot(v,v)) / sum;
|
|
||||||
|
|
||||||
for (int k = 0; k < 8; ++k) {
|
|
||||||
float wk = w[k] * g;
|
|
||||||
m[k] += vec4(c * wk, wk);
|
|
||||||
s[k] += c * c * wk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 output = vec4(0.0f);
|
|
||||||
for (int k = 0; k < n; ++k) {
|
|
||||||
m[k].rgb /= m[k].w;
|
|
||||||
s[k] = abs(s[k] / m[k].w - m[k].rgb * m[k].rgb);
|
|
||||||
float sigma2 = s[k].r + s[k].g + s[k].b;
|
|
||||||
float w = 1.0f / (1.0f + pow(hardness * 1000.0f * sigma2, 0.5f * sharpness));
|
|
||||||
output += vec4(m[k].rgb * w, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
return clamp(output / output.w, 0.0f, 1.0f).rgb;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
uid://chqh2cni1qiuu
|
|
|
@ -9,11 +9,9 @@
|
||||||
The fourth value is always alpha.
|
The fourth value is always alpha.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "./common.gdshaderinc"
|
#include "res://shaderlib/common.gdshaderinc"
|
||||||
|
|
||||||
vec4 rgb2oklab(vec4 c) {
|
vec4 rgb2oklab(vec4 c) {
|
||||||
// oklab.x and .y (a and b) should range from -0.5 to 0.5
|
|
||||||
|
|
||||||
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
|
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
|
||||||
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
|
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
|
||||||
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
|
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
|
||||||
|
@ -31,8 +29,6 @@ vec4 rgb2oklab(vec4 c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 oklab2rgb(vec4 c) {
|
vec4 oklab2rgb(vec4 c) {
|
||||||
// oklab.x and .y (a and b) should range from -0.5 to 0.5
|
|
||||||
|
|
||||||
float l_ = c.x + 0.3963377774f * c.y + 0.2158037573f * c.z;
|
float l_ = c.x + 0.3963377774f * c.y + 0.2158037573f * c.z;
|
||||||
float m_ = c.x - 0.1055613458f * c.y - 0.0638541728f * c.z;
|
float m_ = c.x - 0.1055613458f * c.y - 0.0638541728f * c.z;
|
||||||
float s_ = c.x - 0.0894841775f * c.y - 1.2914855480f * c.z;
|
float s_ = c.x - 0.0894841775f * c.y - 1.2914855480f * c.z;
|
||||||
|
@ -50,7 +46,6 @@ vec4 oklab2rgb(vec4 c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 oklab2oklch(vec4 c) {
|
vec4 oklab2oklch(vec4 c) {
|
||||||
// oklch.z (hue) ranges from -3.6 to 3.6
|
|
||||||
return vec4(
|
return vec4(
|
||||||
c.x,
|
c.x,
|
||||||
sqrt((c.y * c.y) + (c.z * c.z)),
|
sqrt((c.y * c.y) + (c.z * c.z)),
|
||||||
|
@ -60,7 +55,6 @@ vec4 oklab2oklch(vec4 c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 oklch2oklab(vec4 c) {
|
vec4 oklch2oklab(vec4 c) {
|
||||||
// oklch.z (hue) ranges from -3.6 to 3.6
|
|
||||||
return vec4(
|
return vec4(
|
||||||
c.x,
|
c.x,
|
||||||
c.y * cos(c.z),
|
c.y * cos(c.z),
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://ckw4nfslk4m6l
|
|
|
@ -1 +0,0 @@
|
||||||
uid://dpu5nneo5bgnq
|
|
|
@ -1,126 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
Pixelsorting using odd-even sort
|
|
||||||
|
|
||||||
I roughly followed https://ciphrd.com/2020/04/08/pixel-sorting-on-shader-using-well-crafted-sorting-filters-glsl/
|
|
||||||
- vector fields aren't implemented, diagonal sorting is not supported!
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "./hsv.gdshaderinc"
|
|
||||||
#include "./oklab.gdshaderinc"
|
|
||||||
|
|
||||||
#define INF (1.0/0.0)
|
|
||||||
|
|
||||||
#define DIRECTION_LEFT_TO_RIGHT vec2(1, 0)
|
|
||||||
#define DIRECTION_RIGHT_TO_LEFT vec2(-1, 0)
|
|
||||||
#define DIRECTION_TOP_TO_BOTTOM vec2(0, 1)
|
|
||||||
#define DIRECTION_BOTTOM_TO_TOP vec2(0, -1)
|
|
||||||
|
|
||||||
#define COLOR_MODE_RGB 0
|
|
||||||
#define COLOR_MODE_OKLAB 1
|
|
||||||
#define COLOR_MODE_OKLCH 2
|
|
||||||
#define COLOR_MODE_HSV 3
|
|
||||||
|
|
||||||
vec4 pixelsort_step(
|
|
||||||
sampler2D tex, vec2 uv,
|
|
||||||
vec2 direction, // e.g. (1, 0) for left-to-right or (0, -1) for bottom-to-top
|
|
||||||
// see DIRECTION_LEFT_TO_RIGHT, etc.
|
|
||||||
// note: vertical sorting doesn't work, so using e.g. (1, 1) won't work
|
|
||||||
int color_mode, // 0 = RGB, 1 = OKLAB, 2 = OKLCH, 3 = HSV
|
|
||||||
// see COLOR_MODE_RGB, etc.
|
|
||||||
bool color_channel_mask[3], // which color channel(s) to take into account
|
|
||||||
float lower_threshold[3], // lower threshold for pixels to be considered sorted
|
|
||||||
// when in doubt, use {-INF, -INF, -INF}
|
|
||||||
float upper_threshold[3], // upper threshold; {INF, INF, INF}
|
|
||||||
int step_ // from STEP
|
|
||||||
) {
|
|
||||||
// sanitize inputs
|
|
||||||
direction = clamp(direction, vec2(-1, -1), vec2(1, 1));
|
|
||||||
color_mode = clamp(color_mode, 0, 3);
|
|
||||||
// get neighbour
|
|
||||||
vec2 texture_size = vec2(textureSize(tex, 0));
|
|
||||||
vec2 a = (mod(floor(uv * texture_size), 2.0) * 2.0 - 1.0) * (mod(float(step_), 2.0) * 2.0 - 1.0);
|
|
||||||
vec2 neighbour_uv = uv + (direction * a / texture_size);
|
|
||||||
//
|
|
||||||
vec4 x = texture(tex, uv);
|
|
||||||
vec4 y = texture(tex, neighbour_uv);
|
|
||||||
if ( // stop at borders
|
|
||||||
neighbour_uv.x > 1.0 ||
|
|
||||||
neighbour_uv.x < 0.0 ||
|
|
||||||
neighbour_uv.y > 1.0 ||
|
|
||||||
neighbour_uv.y < 0.0
|
|
||||||
) {
|
|
||||||
return x;
|
|
||||||
} else {
|
|
||||||
// convert color if necessary
|
|
||||||
// get value to compare
|
|
||||||
float vx = 0.0;
|
|
||||||
float vy = 0.0;
|
|
||||||
vec3 color_x;
|
|
||||||
vec3 color_y;
|
|
||||||
if (color_mode == COLOR_MODE_RGB) {
|
|
||||||
color_x = x.rgb;
|
|
||||||
color_y = y.rgb;
|
|
||||||
} else if (color_mode == COLOR_MODE_OKLAB) {
|
|
||||||
color_x = rgb2oklab(x).rgb;
|
|
||||||
color_y = rgb2oklab(y).rgb;
|
|
||||||
} else if (color_mode == COLOR_MODE_OKLCH) {
|
|
||||||
color_x = oklab2oklch(rgb2oklab(x)).rgb;
|
|
||||||
color_y = oklab2oklch(rgb2oklab(y)).rgb;
|
|
||||||
} else if (color_mode == COLOR_MODE_HSV) {
|
|
||||||
color_x = rgb2hsv(x).rgb;
|
|
||||||
color_y = rgb2hsv(y).rgb;
|
|
||||||
}
|
|
||||||
float divisor = 0.0;
|
|
||||||
if (color_channel_mask[0]) {
|
|
||||||
vx += color_x.r;
|
|
||||||
vy += color_y.r;
|
|
||||||
divisor += 1.0;
|
|
||||||
}
|
|
||||||
if (color_channel_mask[1]) {
|
|
||||||
vx += color_x.g;
|
|
||||||
vy += color_y.g;
|
|
||||||
divisor += 1.0;
|
|
||||||
}
|
|
||||||
if (color_channel_mask[2]) {
|
|
||||||
vx += color_x.b;
|
|
||||||
vy += color_y.b;
|
|
||||||
divisor += 1.0;
|
|
||||||
}
|
|
||||||
divisor = max(divisor, 1.0);
|
|
||||||
vx /= divisor;
|
|
||||||
vy /= divisor;
|
|
||||||
//
|
|
||||||
if (
|
|
||||||
(a.x < .0 && abs(direction).y == .0) ||
|
|
||||||
(a.y < .0 && abs(direction).x == .0)
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
vy > vx &&
|
|
||||||
// threshold
|
|
||||||
color_x.r < upper_threshold[0] &&
|
|
||||||
color_x.g < upper_threshold[1] &&
|
|
||||||
color_x.b < upper_threshold[2] &&
|
|
||||||
color_x.r > lower_threshold[0] &&
|
|
||||||
color_x.g > lower_threshold[1] &&
|
|
||||||
color_x.b > lower_threshold[2]
|
|
||||||
) { return y; }
|
|
||||||
else { return x; }
|
|
||||||
} else if (
|
|
||||||
(a.x > .0 && abs(direction).y == .0) ||
|
|
||||||
(a.y > .0 && abs(direction).x == .0)
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
vx >= vy &&
|
|
||||||
// threshold
|
|
||||||
color_y.r < upper_threshold[0] &&
|
|
||||||
color_y.g < upper_threshold[1] &&
|
|
||||||
color_y.b < upper_threshold[2] &&
|
|
||||||
color_y.r > lower_threshold[0] &&
|
|
||||||
color_y.g > lower_threshold[1] &&
|
|
||||||
color_y.b > lower_threshold[2]
|
|
||||||
) { return y; }
|
|
||||||
else { return x; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
uid://doefnwk3vyr0o
|
|
|
@ -1 +0,0 @@
|
||||||
uid://51u2hjq62e5i
|
|
|
@ -1,50 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
Edge Detection (Sobel Filter and Gaussian Blur) by FencerDevLog, adapted
|
|
||||||
original code: https://godotshaders.com/shader/edge-detection-sobel-filter-and-gaussian-blur/
|
|
||||||
license of the original code: CC0
|
|
||||||
*/
|
|
||||||
|
|
||||||
vec3 _convolution(sampler2D tex, vec2 uv, vec2 pixel_size) {
|
|
||||||
vec3 conv = vec3(0.0);
|
|
||||||
// Gaussian blur kernel
|
|
||||||
float gauss[25] = {
|
|
||||||
0.00390625, 0.015625, 0.0234375, 0.015625, 0.00390625,
|
|
||||||
0.015625, 0.0625, 0.09375, 0.0625, 0.015625,
|
|
||||||
0.0234375, 0.09375, 0.140625, 0.09375, 0.0234375,
|
|
||||||
0.015625, 0.0625, 0.09375, 0.0625, 0.015625,
|
|
||||||
0.00390625, 0.015625, 0.0234375, 0.015625, 0.00390625
|
|
||||||
};
|
|
||||||
for (int row = 0; row < 5; row++) {
|
|
||||||
for (int col = 0; col < 5; col++) {
|
|
||||||
conv += texture(tex, uv + vec2(float(col - 2), float(row - 2)) * pixel_size).rgb * gauss[row * 5 + col];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return conv;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 sobel(sampler2D tex, vec2 uv) {
|
|
||||||
vec2 pixel_size = 1.0/vec2(textureSize(tex, 0));
|
|
||||||
vec3 pixels[9]; // Sobel kernel
|
|
||||||
// [0, 1, 2]
|
|
||||||
// [3, 4, 5]
|
|
||||||
// [6, 7, 8]
|
|
||||||
for (int row = 0; row < 3; row++) {
|
|
||||||
for (int col = 0; col < 3; col++) {
|
|
||||||
vec2 uv_ = uv + vec2(float(col - 1), float(row - 1)) * pixel_size;
|
|
||||||
pixels[row * 3 + col] = _convolution(tex, uv_, pixel_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sobel operator
|
|
||||||
vec3 gx = (
|
|
||||||
pixels[0] * -1.0 + pixels[3] * -2.0 + pixels[6] * -1.0
|
|
||||||
+ pixels[2] * 1.0 + pixels[5] * 2.0 + pixels[8] * 1.0
|
|
||||||
);
|
|
||||||
vec3 gy = (
|
|
||||||
pixels[0] * -1.0 + pixels[1] * -2.0 + pixels[2] * -1.0
|
|
||||||
+ pixels[6] * 1.0 + pixels[7] * 2.0 + pixels[8] * 1.0
|
|
||||||
);
|
|
||||||
vec3 sobel = sqrt(gx * gx + gy * gy);
|
|
||||||
return vec4(sobel, 1.0);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
uid://bqo1fpunnl05f
|
|
|
@ -1,9 +1,9 @@
|
||||||
extends Camera2D
|
extends Camera2D
|
||||||
|
|
||||||
@onready var image_viewport_display = %ImageViewportDisplay
|
|
||||||
|
|
||||||
var drag = false
|
var drag = false
|
||||||
|
|
||||||
|
@onready var image_viewport_display = %ImageViewportDisplay
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event.is_action_pressed("zoom_out"):
|
if event.is_action_pressed("zoom_out"):
|
||||||
zoom_out()
|
zoom_out()
|
||||||
|
@ -16,35 +16,31 @@ func _input(event):
|
||||||
if self.drag && event is InputEventMouseMotion:
|
if self.drag && event is InputEventMouseMotion:
|
||||||
self.global_position -= event.relative / self.zoom
|
self.global_position -= event.relative / self.zoom
|
||||||
|
|
||||||
var old_zoom = self.zoom
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
|
||||||
if self.zoom != old_zoom:
|
|
||||||
image_viewport_display.update_zoom_texture_filter(self.zoom)
|
|
||||||
image_viewport_display.material.set_shader_parameter("zoom_level", self.zoom)
|
|
||||||
old_zoom = self.zoom
|
|
||||||
|
|
||||||
func fit_image():
|
func fit_image():
|
||||||
if Filesystem.original_image != null:
|
if Filesystem.original_image != null:
|
||||||
var image_size = Filesystem.original_image.get_size()
|
var image_size = Filesystem.original_image.get_size()
|
||||||
var viewport_size = get_viewport_rect().size
|
var viewport_size = get_viewport_rect().size
|
||||||
var zoomf = 1.0
|
var zoomf = viewport_size.x / image_size.x / 1.1
|
||||||
if viewport_size.x / image_size.x * image_size.y > viewport_size.y:
|
if zoomf * image_size.y > viewport_size.y:
|
||||||
zoomf = viewport_size.y / image_size.y / 1.25
|
zoomf = viewport_size.y / image_size.y / 1.1
|
||||||
else:
|
|
||||||
zoomf = viewport_size.x / image_size.x / 1.2
|
|
||||||
self.zoom = Vector2(zoomf, zoomf)
|
self.zoom = Vector2(zoomf, zoomf)
|
||||||
self.global_position = Vector2(0, 0)
|
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():
|
func zoom_in():
|
||||||
var old_mouse_pos = get_global_mouse_position()
|
var old_mouse_pos = get_global_mouse_position()
|
||||||
self.zoom *= 1.2
|
self.zoom *= 1.2
|
||||||
self.global_position += old_mouse_pos - get_global_mouse_position()
|
self.global_position += old_mouse_pos - get_global_mouse_position()
|
||||||
|
update_vd_zoomlevel()
|
||||||
|
|
||||||
func zoom_out():
|
func zoom_out():
|
||||||
var old_mouse_pos = get_global_mouse_position()
|
var old_mouse_pos = get_global_mouse_position()
|
||||||
self.zoom *= 1/1.2
|
self.zoom *= 1/1.2
|
||||||
self.global_position += old_mouse_pos - get_global_mouse_position()
|
self.global_position += old_mouse_pos - get_global_mouse_position()
|
||||||
|
update_vd_zoomlevel()
|
||||||
|
|
||||||
func _on_fit_image_button_pressed():
|
func _on_fit_image_button_pressed():
|
||||||
fit_image()
|
fit_image()
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://b6r8rigubdctk
|
|
306
src/Editor.gd
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
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 save_image_button = %SaveImageButton
|
||||||
|
|
||||||
|
@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 #
|
||||||
|
# https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_language.cpp
|
||||||
|
# https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_types.cpp
|
||||||
|
#
|
||||||
|
const gdshader_boolean_values = [
|
||||||
|
"true", "false"
|
||||||
|
]
|
||||||
|
const gdshader_datatypes = [
|
||||||
|
"void",
|
||||||
|
"bool", "bvec2", "bvec3", "bvec4",
|
||||||
|
"int", "ivec2", "ivec3", "ivec4",
|
||||||
|
"uint", "uvec2", "uvec3", "uvec4",
|
||||||
|
"float", "vec2", "vec3", "vec4",
|
||||||
|
"mat2", "mat3", "mat4",
|
||||||
|
"sampler2D", "isampler2D", "usampler2D",
|
||||||
|
"sampler2DArray", "isampler2DArray", "usampler2DArray",
|
||||||
|
]
|
||||||
|
const gdshader_precision_modifiers = [
|
||||||
|
"lowp", "mediump", "heighp"
|
||||||
|
]
|
||||||
|
const gdshader_global_space_keywords = [
|
||||||
|
"uniform", "group_uniforms", "varying", "const",
|
||||||
|
"struct", "shader_type", "render_mode"
|
||||||
|
]
|
||||||
|
const gdshader_uniform_qualifiers = [
|
||||||
|
"instance", "global"
|
||||||
|
]
|
||||||
|
const gdshader_block_keywords = [
|
||||||
|
"if", "else",
|
||||||
|
"for", "while", "do",
|
||||||
|
"switch", "case",
|
||||||
|
"default", "break", "continue",
|
||||||
|
"return", "discard"
|
||||||
|
]
|
||||||
|
const gdshader_function_specifier_keywords = [
|
||||||
|
"in", "out", "inout"
|
||||||
|
]
|
||||||
|
const gdshader_hints = [
|
||||||
|
"source_color", "hint_range", "instance_index"
|
||||||
|
]
|
||||||
|
const gdshader_sampler_hints = [
|
||||||
|
"hint_normal",
|
||||||
|
"hint_default_white", "hint_default_black", "hint_default_transparent",
|
||||||
|
"hint_anisotropy",
|
||||||
|
"hint_roughness_r", "hint_roughness_g", "hint_roughness_b", "hint_roughness_a",
|
||||||
|
"hint_roughness_normal", "hint_roughness_gray",
|
||||||
|
"hint_screen_texture", "hint_normal_roughness_texture",
|
||||||
|
"hint_depth_texture",
|
||||||
|
"filter_nearest", "filter_linear",
|
||||||
|
"filter_nearest_mipmap", "filter_linear_mipmap",
|
||||||
|
"filter_nearest_mipmap_anisotropic", "filter_linear_mipmap_anisotropic",
|
||||||
|
"repeat_enable", "repeat_disable"
|
||||||
|
]
|
||||||
|
const gdshader_builtin_functions = [
|
||||||
|
"radians", "degrees",
|
||||||
|
"sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh",
|
||||||
|
"asinh", "acosh", "atanh",
|
||||||
|
"pow", "exp", "exp2", "log", "log2", "sqrt", "inversesqrt",
|
||||||
|
"abs", "sign", "floor", "trunc", "round", "roundEven", "ceil", "fract",
|
||||||
|
"mod", "modf", "min", "max", "clamp",
|
||||||
|
"mix", "step", "smoothstep",
|
||||||
|
"isnan", "isinf",
|
||||||
|
"floatBitsToInt", "floatBitsToUint", "intBitsToFloat", "uintBitsToFloat",
|
||||||
|
"length", "distance",
|
||||||
|
"dot", "cross",
|
||||||
|
"normalize", "reflect", "refract", "faceforward",
|
||||||
|
"matrixCompMult", "outerProduct", "transpose",
|
||||||
|
"determinant", "inverse",
|
||||||
|
"lessThan", "greaterThan", "lessThanEqual", "greaterThanEqual",
|
||||||
|
"equal", "notEqual",
|
||||||
|
"any", "all", "not",
|
||||||
|
"textureSize", "texture", "textureProj", "textureLod", "texelFetch",
|
||||||
|
"textureProjLod", "textureGrad", "textureProjGrad", "textureGather",
|
||||||
|
"textureQueryLod", "textureQueryLevels",
|
||||||
|
"dFdx", "dFdxCoarse", "dFdxFine", "dFdy", "dFdyCoarse", "dFdyFine",
|
||||||
|
"fwidth", "fwidthCoarse", "fwidthFine"
|
||||||
|
]
|
||||||
|
const gdshader_sub_functions = [
|
||||||
|
"length", "fma",
|
||||||
|
"packHalf2x16", "packUnorm2x16", "packSnorm2x16", "packUnorm4x8", "packSnorm4x8",
|
||||||
|
"unpackHalf2x16", "unpackUnorm2x16", "unpackSnorm2x16", "unpackUnorm4x8", "unpackSnorm4x8",
|
||||||
|
"bitfieldExtract", "bitfieldInsert", "bitfieldReverse", "bitCount",
|
||||||
|
"findLSB", "findMSB",
|
||||||
|
"umulExtended", "imulExtended",
|
||||||
|
"uaddCarry", "usubBorrow",
|
||||||
|
"ldexp", "frexp"
|
||||||
|
]
|
||||||
|
const gdshader_builtins = [
|
||||||
|
"TIME", "PI", "TAU", "E",
|
||||||
|
"VERTEX",
|
||||||
|
"UV",
|
||||||
|
"COLOR",
|
||||||
|
"POINT_SIZE",
|
||||||
|
"AT_LIGHT_PASS",
|
||||||
|
"TEXTURE_PIXEL_SIZE",
|
||||||
|
"SHADOW_VERTEX", "LIGHT_VERTEX",
|
||||||
|
"FRAGCOORD",
|
||||||
|
"NORMAL", "NORMAL_MAP", "NORMAL_MAP_DEPTH",
|
||||||
|
"TEXTURE",
|
||||||
|
"POINT_COORD",
|
||||||
|
"SPECULAR_SHININESS"
|
||||||
|
]
|
||||||
|
const gdshader_preprocessor = [
|
||||||
|
"define", "undef", "include", "pragma",
|
||||||
|
"if", "elif", "ifdef", "ifndef", "else", "endif"
|
||||||
|
]
|
||||||
|
# shaderlib
|
||||||
|
var shaderlib_regex = {} # auto-generated
|
||||||
|
const shaderlib_functions = {
|
||||||
|
"blur": ["gaussian_blur"],
|
||||||
|
"common": ["alpha_blend", "cbrt"],
|
||||||
|
"denoise": ["smart_denoise"],
|
||||||
|
"hsv": ["rgb2hsv", "hsv2rgb",],
|
||||||
|
"oklab": ["oklab2rgb", "rgb2oklab", "oklab2oklch", "oklch2oklab"],
|
||||||
|
"pixelate": ["pixelate"],
|
||||||
|
"place_texture": ["place_texture"],
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# configure Highlighter
|
||||||
|
#
|
||||||
|
class ShaderSyntaxHighlighter extends CodeHighlighter:
|
||||||
|
func _init():
|
||||||
|
add_color_region("//", "", Color.WEB_GRAY, true)
|
||||||
|
add_color_region("/*", "*/", Color.WEB_GRAY, false)
|
||||||
|
function_color = Color.INDIAN_RED
|
||||||
|
for k in gdshader_boolean_values:
|
||||||
|
keyword_colors[k] = Color.INDIAN_RED
|
||||||
|
for k in ( gdshader_datatypes
|
||||||
|
+ gdshader_hints
|
||||||
|
+ gdshader_sampler_hints
|
||||||
|
+ gdshader_global_space_keywords
|
||||||
|
+ gdshader_function_specifier_keywords
|
||||||
|
+ gdshader_precision_modifiers
|
||||||
|
+ gdshader_uniform_qualifiers):
|
||||||
|
keyword_colors[k] = Color.ORCHID;
|
||||||
|
for k in gdshader_block_keywords:
|
||||||
|
keyword_colors[k] = Color.CORAL
|
||||||
|
for k in gdshader_builtins:
|
||||||
|
keyword_colors[k] = Color.DARK_TURQUOISE
|
||||||
|
member_variable_color = Color.LIGHT_BLUE
|
||||||
|
number_color = Color.AQUA
|
||||||
|
symbol_color = Color.GRAY
|
||||||
|
#
|
||||||
|
# and code completion
|
||||||
|
#
|
||||||
|
func _on_code_edit_code_completion_requested():
|
||||||
|
for k in gdshader_boolean_values:
|
||||||
|
code_editor.code_completion_prefixes.append(k)
|
||||||
|
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.INDIAN_RED)
|
||||||
|
for k in ( gdshader_datatypes
|
||||||
|
+ gdshader_hints
|
||||||
|
+ gdshader_sampler_hints
|
||||||
|
+ gdshader_global_space_keywords
|
||||||
|
+ gdshader_function_specifier_keywords
|
||||||
|
+ gdshader_precision_modifiers
|
||||||
|
+ gdshader_uniform_qualifiers):
|
||||||
|
code_editor.code_completion_prefixes.append(k)
|
||||||
|
code_editor.add_code_completion_option(CodeEdit.KIND_CLASS, k, k, Color.ORCHID)
|
||||||
|
for k in gdshader_block_keywords:
|
||||||
|
code_editor.code_completion_prefixes.append(k)
|
||||||
|
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.CORAL)
|
||||||
|
for k in gdshader_builtins:
|
||||||
|
code_editor.code_completion_prefixes.append(k)
|
||||||
|
code_editor.add_code_completion_option(CodeEdit.KIND_CONSTANT, k, k, Color.DARK_TURQUOISE)
|
||||||
|
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)
|
||||||
|
for k in gdshader_preprocessor:
|
||||||
|
code_editor.code_completion_prefixes.append(k)
|
||||||
|
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, "#" + k, k)
|
||||||
|
# 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 _ready():
|
||||||
|
# generate regexes
|
||||||
|
for k in shaderlib_functions:
|
||||||
|
shaderlib_regex[k] = RegEx.create_from_string(
|
||||||
|
r'\s*\#include\s+\"res\:\/\/shaderlib\/' + k + r'\.gdshaderinc\"')
|
||||||
|
#
|
||||||
|
code_editor.code_completion_enabled = true
|
||||||
|
code_editor.syntax_highlighter = ShaderSyntaxHighlighter.new()
|
||||||
|
self.update_code_edit()
|
||||||
|
|
||||||
|
func _input(event):
|
||||||
|
if event.is_action_pressed("apply_shader"):
|
||||||
|
_on_apply_shader_button_pressed()
|
||||||
|
elif event.is_action_pressed("save_shader"):
|
||||||
|
accept_event() # Event is now handled.
|
||||||
|
_on_save_shader_button_pressed()
|
||||||
|
|
||||||
|
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():
|
||||||
|
Filesystem.shader_code = code_editor.text
|
||||||
|
if Filesystem.last_shader_savepath == "":
|
||||||
|
_on_save_shader_as_button_pressed()
|
||||||
|
else:
|
||||||
|
_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_fit_image_button_pressed():
|
||||||
|
camera.fit_image()
|
||||||
|
|
||||||
|
func _on_apply_shader_button_pressed():
|
||||||
|
save_image_button.disabled = true
|
||||||
|
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)
|
||||||
|
save_image_button.disabled = false
|
||||||
|
|
||||||
|
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()
|
|
@ -1,31 +1,25 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
var cwd = "."
|
@onready var template_shader: Shader = load("res://src/shader/template.gdshader")
|
||||||
|
@onready var shader_code: String = template_shader.code
|
||||||
var shader_path = "":
|
|
||||||
get():
|
|
||||||
return shader_path
|
|
||||||
set(v):
|
|
||||||
var old = shader_path
|
|
||||||
shader_path = v
|
|
||||||
if "/" in v: # update current working directory
|
|
||||||
cwd = v.substr(0, v.rfind("/"))
|
|
||||||
if old != shader_path:
|
|
||||||
store_last_opened_file()
|
|
||||||
|
|
||||||
var shader: Shader:
|
|
||||||
get():
|
|
||||||
print("Load ", shader_path)
|
|
||||||
return load(shader_path)
|
|
||||||
|
|
||||||
var original_image: ImageTexture
|
var original_image: ImageTexture
|
||||||
|
|
||||||
var additional_images: Dictionary
|
var additional_images: Dictionary
|
||||||
var result: Image
|
var result: Image
|
||||||
|
|
||||||
|
var cwd = "."
|
||||||
var last_image_savepath = ""
|
var last_image_savepath = ""
|
||||||
|
var last_shader_savepath = ""
|
||||||
var last_original_image_path = ""
|
var last_original_image_path = ""
|
||||||
|
|
||||||
|
func reset():
|
||||||
|
self.shader_code = self.template_shader.code
|
||||||
|
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:
|
func get_absolute_path(p: String) -> String:
|
||||||
# this only works on Linux!
|
# this only works on Linux!
|
||||||
if !p.begins_with("/"):
|
if !p.begins_with("/"):
|
||||||
|
@ -33,14 +27,14 @@ func get_absolute_path(p: String) -> String:
|
||||||
return p
|
return p
|
||||||
|
|
||||||
func load_original_image(path: String) -> String: # returns an error message
|
func load_original_image(path: String) -> String: # returns an error message
|
||||||
print("Load ", path)
|
|
||||||
var img = Image.new()
|
var img = Image.new()
|
||||||
var err = img.load(path)
|
var err = img.load(path)
|
||||||
if err == OK:
|
if err == OK:
|
||||||
original_image = ImageTexture.create_from_image(img)
|
original_image = ImageTexture.create_from_image(img)
|
||||||
if self.last_image_savepath == "" or path != self.last_original_image_path:
|
if path != self.last_original_image_path:
|
||||||
self.last_image_savepath = path
|
|
||||||
self.last_original_image_path = path
|
self.last_original_image_path = path
|
||||||
|
if self.last_image_savepath == "":
|
||||||
|
self.last_image_savepath = path
|
||||||
return ""
|
return ""
|
||||||
return error_string(err) + " " + path
|
return error_string(err) + " " + path
|
||||||
|
|
||||||
|
@ -48,7 +42,6 @@ func clear_additional_images():
|
||||||
additional_images.clear()
|
additional_images.clear()
|
||||||
|
|
||||||
func load_additional_image(key: String, path: String) -> String: # returns Error Message String
|
func load_additional_image(key: String, path: String) -> String: # returns Error Message String
|
||||||
print("Load ", path)
|
|
||||||
var img = Image.new()
|
var img = Image.new()
|
||||||
var err = img.load(path)
|
var err = img.load(path)
|
||||||
if err == OK:
|
if err == OK:
|
||||||
|
@ -65,13 +58,33 @@ func save_result(path: String):
|
||||||
else:
|
else:
|
||||||
self.last_image_savepath = path
|
self.last_image_savepath = path
|
||||||
|
|
||||||
|
func load_shader(path: String):
|
||||||
|
print("Load ", path)
|
||||||
|
var file = FileAccess.open(path, FileAccess.READ)
|
||||||
|
if file != null:
|
||||||
|
self.shader_code = file.get_as_text()
|
||||||
|
if "/" in path: # update current working directory
|
||||||
|
self.cwd = path.substr(0, path.rfind("/"))
|
||||||
|
self.last_shader_savepath = path
|
||||||
|
store_last_opened_file()
|
||||||
|
|
||||||
|
func save_shader(path: String):
|
||||||
|
print("Save ", path)
|
||||||
|
var file = FileAccess.open(path, FileAccess.WRITE)
|
||||||
|
file.store_string(self.shader_code)
|
||||||
|
file.flush()
|
||||||
|
if "/" in path: # update current working directory
|
||||||
|
self.cwd = path.substr(0, path.rfind("/"))
|
||||||
|
self.last_shader_savepath = path
|
||||||
|
store_last_opened_file()
|
||||||
|
|
||||||
func store_last_opened_file():
|
func store_last_opened_file():
|
||||||
var f = FileAccess.open("user://last_opened", FileAccess.WRITE)
|
var f = FileAccess.open("user://last_opened", FileAccess.WRITE)
|
||||||
if f != null:
|
if f != null:
|
||||||
f.store_pascal_string(shader_path)
|
f.store_pascal_string(last_shader_savepath)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
func remember_last_opened_file():
|
func remember_last_opened_file():
|
||||||
var f = FileAccess.open("user://last_opened", FileAccess.READ)
|
var f = FileAccess.open("user://last_opened", FileAccess.READ)
|
||||||
if f != null:
|
if f != null:
|
||||||
shader_path = f.get_pascal_string()
|
last_shader_savepath = f.get_pascal_string()
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://rlb041ygdwol
|
|
|
@ -37,26 +37,17 @@ func validate_shader_compilation(shader: Shader) -> bool:
|
||||||
# test if uniform list is empty -> if it is empty, the shader compilation failed
|
# test if uniform list is empty -> if it is empty, the shader compilation failed
|
||||||
return len(shader.get_shader_uniform_list()) > 0
|
return len(shader.get_shader_uniform_list()) > 0
|
||||||
|
|
||||||
func shader_has_uniform(shader: Shader, name: String, type: int) -> bool:
|
func inject_step_uniform(shader_code: String) -> Shader:
|
||||||
for u in shader.get_shader_uniform_list():
|
var shader = Shader.new()
|
||||||
if u["name"] == name && u["type"] == type:
|
# this should run after validate_shader_compilation()
|
||||||
return true
|
var fragment_function_match = _fragment_function_regex.search(shader_code)
|
||||||
return false
|
shader.code = shader_code.insert(fragment_function_match.get_start(), "\nuniform int STEP;")
|
||||||
|
return shader
|
||||||
func set_vsync(enabled: bool):
|
|
||||||
if enabled:
|
|
||||||
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
|
|
||||||
else:
|
|
||||||
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
|
|
||||||
|
|
||||||
func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings)
|
func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings)
|
||||||
var shader = Filesystem.shader # read from disk
|
# inject STEP uniform & get number of steps
|
||||||
if shader == null:
|
var shader: Shader = inject_step_uniform(Filesystem.shader_code)
|
||||||
return ["No shader opened!"]
|
|
||||||
# get number of steps & check if code has STEP uniform
|
|
||||||
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
|
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
|
||||||
var has_step_uniform: bool = shader_has_uniform(shader, "STEP", 2)
|
|
||||||
var has_steps_uniform: bool = shader_has_uniform(shader, "STEPS", 2)
|
|
||||||
# validate shader
|
# validate shader
|
||||||
if not validate_shader_compilation(shader):
|
if not validate_shader_compilation(shader):
|
||||||
return ["Shader compilation failed!"]
|
return ["Shader compilation failed!"]
|
||||||
|
@ -91,9 +82,6 @@ func update(overwrite_image_path: String = "") -> Array: # returns error message
|
||||||
image_sprite.texture = Filesystem.original_image
|
image_sprite.texture = Filesystem.original_image
|
||||||
image_sprite.offset = Filesystem.original_image.get_size() / 2
|
image_sprite.offset = Filesystem.original_image.get_size() / 2
|
||||||
self.size = Filesystem.original_image.get_size()
|
self.size = Filesystem.original_image.get_size()
|
||||||
# already show the image viewport & fit the image
|
|
||||||
if fit_image: camera.fit_image()
|
|
||||||
image_viewport_display.show()
|
|
||||||
# create shader material
|
# create shader material
|
||||||
var mat = ShaderMaterial.new()
|
var mat = ShaderMaterial.new()
|
||||||
mat.shader = shader
|
mat.shader = shader
|
||||||
|
@ -105,19 +93,17 @@ func update(overwrite_image_path: String = "") -> Array: # returns error message
|
||||||
# assign material
|
# assign material
|
||||||
image_sprite.material = mat
|
image_sprite.material = mat
|
||||||
# iterate n times
|
# iterate n times
|
||||||
set_vsync(false) # speed up processing
|
|
||||||
if has_steps_uniform:
|
|
||||||
# set STEPS param
|
|
||||||
mat.set_shader_parameter("STEPS", steps)
|
|
||||||
for i in range(steps):
|
for i in range(steps):
|
||||||
if has_step_uniform:
|
|
||||||
# set STEP param
|
# set STEP param
|
||||||
mat.set_shader_parameter("STEP", i)
|
mat.set_shader_parameter("STEP", i)
|
||||||
# Get viewport texture
|
# Get viewport texture
|
||||||
await RenderingServer.frame_post_draw # wait for next frame to get drawn
|
await RenderingServer.frame_post_draw # wait for next frame to get drawn
|
||||||
Filesystem.result = get_texture().get_image()
|
Filesystem.result = get_texture().get_image()
|
||||||
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
|
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
|
||||||
set_vsync(true) # reenable vsync
|
|
||||||
image_sprite.material = null
|
image_sprite.material = null
|
||||||
|
if fit_image:
|
||||||
|
camera.fit_image()
|
||||||
|
camera.update_vd_zoomlevel()
|
||||||
|
image_viewport_display.show()
|
||||||
# done
|
# done
|
||||||
return errors
|
return errors
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://d106170kuigl3
|
|
|
@ -1 +0,0 @@
|
||||||
uid://ctc4lhbdsoq7u
|
|
41
src/Main.gd
|
@ -4,17 +4,14 @@ const BATCH_MODE_SUPPORTED_EXTS = [
|
||||||
".bmp", ".dds", ".exr", ".hdr", ".jpeg", ".jpg", ".ktx", ".png", ".svg", ".webp"
|
".bmp", ".dds", ".exr", ".hdr", ".jpeg", ".jpg", ".ktx", ".png", ".svg", ".webp"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@onready var editor_window = %EditorWindow
|
||||||
|
@onready var ui_container = %UserInterfaceContainer
|
||||||
@onready var app_name = ProjectSettings.get_setting("application/config/name")
|
@onready var app_name = ProjectSettings.get_setting("application/config/name")
|
||||||
|
|
||||||
func show_help():
|
func show_help():
|
||||||
print(
|
print(
|
||||||
"Usage:\n\n",
|
"Usage:\n\n",
|
||||||
"./Fragmented <command> <args...>\n\n",
|
"./Fragmented cmd --shader PATH [--load-image PATH]\n\n",
|
||||||
"Commands:\n\n",
|
|
||||||
" help\n\n",
|
|
||||||
" | Shows this help text.\n\n",
|
|
||||||
" apply --shader PATH [--load-image PATH]\n\n",
|
|
||||||
" | Applies a shader file.\n\n",
|
|
||||||
" --shader PATH The path to the shader\n",
|
" --shader PATH The path to the shader\n",
|
||||||
" --output PATH Where to write the resulting image to.\n",
|
" --output PATH Where to write the resulting image to.\n",
|
||||||
" In batch mode, this must be a folder.\n",
|
" In batch mode, this must be a folder.\n",
|
||||||
|
@ -44,10 +41,9 @@ func cli_handle_errors(errors: Array) -> int:
|
||||||
printerr(e)
|
printerr(e)
|
||||||
return n_errors
|
return n_errors
|
||||||
|
|
||||||
func cli(args: PackedStringArray):
|
func _ready():
|
||||||
print(
|
var args = OS.get_cmdline_args()
|
||||||
"~ Fragmented CLI ~\n",
|
if "cmd" in args: # commandline interface
|
||||||
"-================-\n")
|
|
||||||
if "help" in args:
|
if "help" in args:
|
||||||
show_help()
|
show_help()
|
||||||
get_tree().quit(1)
|
get_tree().quit(1)
|
||||||
|
@ -71,7 +67,7 @@ func cli(args: PackedStringArray):
|
||||||
else:
|
else:
|
||||||
batch_mode = true
|
batch_mode = true
|
||||||
#
|
#
|
||||||
Filesystem.shader_path = kwargs["--shader"]
|
Filesystem.load_shader(kwargs["--shader"])
|
||||||
#
|
#
|
||||||
if batch_mode:
|
if batch_mode:
|
||||||
var in_dir_path = load_image_dir.get_current_dir()
|
var in_dir_path = load_image_dir.get_current_dir()
|
||||||
|
@ -104,23 +100,24 @@ func cli(args: PackedStringArray):
|
||||||
get_tree().quit(0)
|
get_tree().quit(0)
|
||||||
else:
|
else:
|
||||||
get_tree().quit(1)
|
get_tree().quit(1)
|
||||||
|
else:
|
||||||
func prepare_gui():
|
|
||||||
update_title()
|
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)
|
||||||
|
editor_window.show()
|
||||||
# Load last opened file
|
# Load last opened file
|
||||||
Filesystem.remember_last_opened_file()
|
Filesystem.remember_last_opened_file()
|
||||||
%MainUI._on_apply_shader_button_pressed()
|
if Filesystem.last_shader_savepath != "":
|
||||||
|
ui_container.get_node("Editor")._on_open_shader_dialog_file_selected(Filesystem.last_shader_savepath)
|
||||||
func _ready():
|
|
||||||
var args = OS.get_cmdline_args()
|
|
||||||
if len(args) > 0 and args[0] in ["apply", "help"]:
|
|
||||||
# use the commandline interface
|
|
||||||
cli(args)
|
|
||||||
else:
|
|
||||||
prepare_gui()
|
|
||||||
|
|
||||||
func update_title(current_file: String = ""):
|
func update_title(current_file: String = ""):
|
||||||
if current_file == "":
|
if current_file == "":
|
||||||
get_window().title = app_name + " - Viewer"
|
get_window().title = app_name + " - Viewer"
|
||||||
|
editor_window.title = app_name + " - Editor"
|
||||||
else:
|
else:
|
||||||
get_window().title = current_file + " - " + app_name + " - Viewer"
|
get_window().title = current_file + " - " + app_name + " - Viewer"
|
||||||
|
editor_window.title = current_file + " - " + app_name + " - Editor"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://5sbslwysin5a
|
|
100
src/MainUI.gd
|
@ -1,100 +0,0 @@
|
||||||
extends Control
|
|
||||||
|
|
||||||
@onready var open_shader_dialog = %OpenShaderDialog
|
|
||||||
@onready var save_image_dialog = %SaveImageDialog
|
|
||||||
|
|
||||||
@onready var open_shader_button = %OpenShaderButton
|
|
||||||
@onready var save_image_button = %SaveImageButton
|
|
||||||
@onready var fit_image_button = %FitImageButton
|
|
||||||
@onready var apply_shader_button = %ApplyShaderButton
|
|
||||||
|
|
||||||
@onready var status_indicator = %StatusIndicator
|
|
||||||
@onready var error_msg_dialog = %ErrorMessageDialog
|
|
||||||
|
|
||||||
@onready var main = get_tree().root.get_node("Main")
|
|
||||||
@onready var compositor = %Compositor
|
|
||||||
@onready var camera = %Camera
|
|
||||||
|
|
||||||
var status_okay_texture: CompressedTexture2D = preload("uid://m1omb6g45vst")
|
|
||||||
var status_error_texture: CompressedTexture2D = preload("uid://04iv1gogpuhu")
|
|
||||||
|
|
||||||
enum Status {OKAY, ERROR, UNKNOWN = -1}
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
func _input(event):
|
|
||||||
if event.is_action_pressed("apply_shader"):
|
|
||||||
_on_apply_shader_button_pressed()
|
|
||||||
elif event.is_action_pressed("save_shader"):
|
|
||||||
accept_event() # Event is now handled.
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
func set_buttons_disabled(disabled: bool):
|
|
||||||
for b in [open_shader_button, save_image_button, fit_image_button, apply_shader_button, status_indicator]:
|
|
||||||
b.disabled = disabled
|
|
||||||
|
|
||||||
func _on_open_shader_button_pressed():
|
|
||||||
set_buttons_disabled(true)
|
|
||||||
open_shader_dialog.show()
|
|
||||||
|
|
||||||
func _on_fit_image_button_pressed():
|
|
||||||
camera.fit_image()
|
|
||||||
|
|
||||||
func _on_apply_shader_button_pressed():
|
|
||||||
set_buttons_disabled(true)
|
|
||||||
var errors = await compositor.update()
|
|
||||||
set_buttons_disabled(false)
|
|
||||||
if len(errors) > 0:
|
|
||||||
update_status(Status.ERROR, "\n".join(errors))
|
|
||||||
else:
|
|
||||||
update_status(Status.OKAY)
|
|
||||||
status_indicator.disabled = true
|
|
||||||
|
|
||||||
func _on_save_image_button_pressed():
|
|
||||||
if Filesystem.result != null:
|
|
||||||
set_buttons_disabled(true)
|
|
||||||
save_image_dialog.current_path = Filesystem.last_image_savepath
|
|
||||||
save_image_dialog.show()
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
func _on_open_shader_dialog_file_selected(path: String):
|
|
||||||
Filesystem.shader_path = path
|
|
||||||
main.update_title(path.split("/")[-1])
|
|
||||||
self._on_apply_shader_button_pressed()
|
|
||||||
|
|
||||||
func _on_open_shader_dialog_canceled() -> void:
|
|
||||||
set_buttons_disabled(false)
|
|
||||||
|
|
||||||
func _on_open_shader_dialog_confirmed() -> void:
|
|
||||||
set_buttons_disabled(false)
|
|
||||||
|
|
||||||
func _on_save_image_dialog_file_selected(path):
|
|
||||||
Filesystem.save_result(path)
|
|
||||||
set_buttons_disabled(false)
|
|
||||||
|
|
||||||
func _on_save_image_dialog_canceled() -> void:
|
|
||||||
set_buttons_disabled(false)
|
|
||||||
|
|
||||||
func _on_save_image_dialog_confirmed() -> void:
|
|
||||||
set_buttons_disabled(false)
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
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_status_indicator_pressed() -> void:
|
|
||||||
error_msg_dialog.show()
|
|
|
@ -1 +0,0 @@
|
||||||
uid://bxgmf2ny7yuc8
|
|
|
@ -1 +0,0 @@
|
||||||
uid://dw8bep14j4j3w
|
|
4
src/UIAppVersion.gd
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
extends Label
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
text = ProjectSettings.get_setting("application/config/version")
|
4
src/UIWindow.gd
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
extends Window
|
||||||
|
|
||||||
|
func _on_close_requested() -> void:
|
||||||
|
get_tree().quit()
|
|
@ -1,8 +0,0 @@
|
||||||
extends Label
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
text = ProjectSettings.get_setting("application/config/name") \
|
|
||||||
+ " " \
|
|
||||||
+ ProjectSettings.get_setting("application/config/version") \
|
|
||||||
+ " | Godot " \
|
|
||||||
+ Engine.get_version_info()["string"]
|
|
|
@ -1 +0,0 @@
|
||||||
uid://bh0gpu3i2p47f
|
|
Before Width: | Height: | Size: 532 B |
|
@ -1,34 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://d2nwchyd6huob"
|
|
||||||
path="res://.godot/imported/bg.png-7c8713dd1fab321784216191fa747e53.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://src/assets/bg.png"
|
|
||||||
dest_files=["res://.godot/imported/bg.png-7c8713dd1fab321784216191fa747e53.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
|
|
|
@ -1,194 +0,0 @@
|
||||||
[gd_scene load_steps=13 format=3 uid="uid://bjah7k4bxo044"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://5sbslwysin5a" path="res://src/Main.gd" id="1_64y3g"]
|
|
||||||
[ext_resource type="Script" uid="uid://d106170kuigl3" path="res://src/ImageCompositor.gd" id="2_4ykh7"]
|
|
||||||
[ext_resource type="Shader" uid="uid://ctk7jomfyx0fh" path="res://src/shader/ivd_outline.gdshader" id="3_0fllm"]
|
|
||||||
[ext_resource type="Script" uid="uid://ctc4lhbdsoq7u" path="res://src/ImageViewportDisplay.gd" id="4_pbpx2"]
|
|
||||||
[ext_resource type="Script" uid="uid://b6r8rigubdctk" path="res://src/Camera.gd" id="5_hkdq6"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://d2nwchyd6huob" path="res://src/assets/bg.png" id="6_kokaf"]
|
|
||||||
[ext_resource type="Theme" uid="uid://cwqlns34rj3vx" path="res://src/theme.tres" id="6_rjp5f"]
|
|
||||||
[ext_resource type="Script" uid="uid://bxgmf2ny7yuc8" path="res://src/MainUI.gd" id="7_5puhk"]
|
|
||||||
[ext_resource type="Script" uid="uid://bh0gpu3i2p47f" path="res://src/VersionLabel.gd" id="8_kod8x"]
|
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_y2ea0"]
|
|
||||||
shader = ExtResource("3_0fllm")
|
|
||||||
shader_parameter/zoom_level = Vector2(1, 1)
|
|
||||||
|
|
||||||
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
|
|
||||||
viewport_path = NodePath("Compositor")
|
|
||||||
|
|
||||||
[sub_resource type="LabelSettings" id="LabelSettings_6o860"]
|
|
||||||
font_size = 12
|
|
||||||
shadow_color = Color(0, 0, 0, 1)
|
|
||||||
|
|
||||||
[node name="Main" type="Node2D"]
|
|
||||||
script = ExtResource("1_64y3g")
|
|
||||||
|
|
||||||
[node name="Compositor" type="SubViewport" parent="."]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
script = ExtResource("2_4ykh7")
|
|
||||||
|
|
||||||
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
material = SubResource("ShaderMaterial_y2ea0")
|
|
||||||
texture = SubResource("ViewportTexture_lct1c")
|
|
||||||
script = ExtResource("4_pbpx2")
|
|
||||||
|
|
||||||
[node name="Camera" type="Camera2D" parent="."]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
offset = Vector2(0, -64)
|
|
||||||
script = ExtResource("5_hkdq6")
|
|
||||||
|
|
||||||
[node name="CanvasLayerBg" type="CanvasLayer" parent="."]
|
|
||||||
layer = -1
|
|
||||||
|
|
||||||
[node name="Control" type="Control" parent="CanvasLayerBg"]
|
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="CanvasLayerBg/Control"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
texture = ExtResource("6_kokaf")
|
|
||||||
stretch_mode = 1
|
|
||||||
|
|
||||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
|
||||||
|
|
||||||
[node name="MainUI" type="Control" parent="CanvasLayer"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
pivot_offset = Vector2(320, 320)
|
|
||||||
theme = ExtResource("6_rjp5f")
|
|
||||||
script = ExtResource("7_5puhk")
|
|
||||||
|
|
||||||
[node name="OpenShaderDialog" type="FileDialog" parent="CanvasLayer/MainUI"]
|
|
||||||
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="SaveImageDialog" type="FileDialog" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
auto_translate_mode = 1
|
|
||||||
title = "Export Image"
|
|
||||||
size = Vector2i(661, 175)
|
|
||||||
mode_overrides_title = false
|
|
||||||
access = 2
|
|
||||||
filters = PackedStringArray("*.png")
|
|
||||||
use_native_dialog = true
|
|
||||||
|
|
||||||
[node name="ErrorMessageDialog" type="AcceptDialog" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
auto_translate_mode = 1
|
|
||||||
title = "Status"
|
|
||||||
initial_position = 2
|
|
||||||
size = Vector2i(256, 128)
|
|
||||||
popup_window = true
|
|
||||||
ok_button_text = "Close"
|
|
||||||
|
|
||||||
[node name="OpenShaderButton" type="Button" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
offset_left = 16.0
|
|
||||||
offset_top = 16.0
|
|
||||||
offset_right = 128.0
|
|
||||||
offset_bottom = 48.0
|
|
||||||
text = "Open Shader"
|
|
||||||
|
|
||||||
[node name="SaveImageButton" type="Button" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
offset_left = 144.0
|
|
||||||
offset_top = 16.0
|
|
||||||
offset_right = 216.0
|
|
||||||
offset_bottom = 48.0
|
|
||||||
disabled = true
|
|
||||||
text = "Export"
|
|
||||||
|
|
||||||
[node name="FitImageButton" type="Button" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 1
|
|
||||||
anchor_left = 1.0
|
|
||||||
anchor_right = 1.0
|
|
||||||
offset_left = -176.0
|
|
||||||
offset_top = 16.0
|
|
||||||
offset_right = -128.0
|
|
||||||
offset_bottom = 48.0
|
|
||||||
grow_horizontal = 0
|
|
||||||
text = "Fit"
|
|
||||||
|
|
||||||
[node name="ApplyShaderButton" type="Button" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 1
|
|
||||||
anchor_left = 1.0
|
|
||||||
anchor_right = 1.0
|
|
||||||
offset_left = -112.0
|
|
||||||
offset_top = 16.0
|
|
||||||
offset_right = -16.0
|
|
||||||
offset_bottom = 48.0
|
|
||||||
grow_horizontal = 0
|
|
||||||
text = "Apply (F5)"
|
|
||||||
|
|
||||||
[node name="StatusIndicator" type="TextureButton" parent="CanvasLayer/MainUI"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 1
|
|
||||||
anchor_left = 1.0
|
|
||||||
anchor_right = 1.0
|
|
||||||
offset_left = -220.0
|
|
||||||
offset_top = 21.0
|
|
||||||
offset_right = -196.0
|
|
||||||
offset_bottom = 45.0
|
|
||||||
grow_horizontal = 0
|
|
||||||
disabled = true
|
|
||||||
ignore_texture_size = true
|
|
||||||
stretch_mode = 0
|
|
||||||
|
|
||||||
[node name="VersionLabel" type="Label" parent="CanvasLayer/MainUI"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 12
|
|
||||||
anchor_top = 1.0
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
offset_left = 16.0
|
|
||||||
offset_top = -24.0
|
|
||||||
offset_right = -16.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 0
|
|
||||||
label_settings = SubResource("LabelSettings_6o860")
|
|
||||||
horizontal_alignment = 1
|
|
||||||
vertical_alignment = 1
|
|
||||||
script = ExtResource("8_kod8x")
|
|
||||||
|
|
||||||
[connection signal="canceled" from="CanvasLayer/MainUI/OpenShaderDialog" to="CanvasLayer/MainUI" method="_on_open_shader_dialog_canceled"]
|
|
||||||
[connection signal="confirmed" from="CanvasLayer/MainUI/OpenShaderDialog" to="CanvasLayer/MainUI" method="_on_open_shader_dialog_confirmed"]
|
|
||||||
[connection signal="file_selected" from="CanvasLayer/MainUI/OpenShaderDialog" to="CanvasLayer/MainUI" method="_on_open_shader_dialog_file_selected"]
|
|
||||||
[connection signal="canceled" from="CanvasLayer/MainUI/SaveImageDialog" to="CanvasLayer/MainUI" method="_on_save_image_dialog_canceled"]
|
|
||||||
[connection signal="confirmed" from="CanvasLayer/MainUI/SaveImageDialog" to="CanvasLayer/MainUI" method="_on_save_image_dialog_confirmed"]
|
|
||||||
[connection signal="file_selected" from="CanvasLayer/MainUI/SaveImageDialog" to="CanvasLayer/MainUI" method="_on_save_image_dialog_file_selected"]
|
|
||||||
[connection signal="pressed" from="CanvasLayer/MainUI/OpenShaderButton" to="CanvasLayer/MainUI" method="_on_open_shader_button_pressed"]
|
|
||||||
[connection signal="pressed" from="CanvasLayer/MainUI/SaveImageButton" to="CanvasLayer/MainUI" method="_on_save_image_button_pressed"]
|
|
||||||
[connection signal="pressed" from="CanvasLayer/MainUI/FitImageButton" to="CanvasLayer/MainUI" method="_on_fit_image_button_pressed"]
|
|
||||||
[connection signal="pressed" from="CanvasLayer/MainUI/ApplyShaderButton" to="CanvasLayer/MainUI" method="_on_apply_shader_button_pressed"]
|
|
||||||
[connection signal="pressed" from="CanvasLayer/MainUI/StatusIndicator" to="CanvasLayer/MainUI" method="_on_status_indicator_pressed"]
|
|
|
@ -11,6 +11,6 @@ void fragment() {
|
||||||
UV.x > 1.0-t.x ||
|
UV.x > 1.0-t.x ||
|
||||||
UV.y > 1.0-t.y
|
UV.y > 1.0-t.y
|
||||||
) {
|
) {
|
||||||
COLOR = mix(COLOR, vec4(0.5), 0.5);
|
COLOR = mix(COLOR, vec4(1.0), 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uid://ctk7jomfyx0fh
|
|
7
src/shader/template.gdshader
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
//!load /path/to/your/image
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
// Called for every pixel the material is visible on.
|
||||||
|
}
|
|
@ -1,57 +0,0 @@
|
||||||
[gd_resource type="Theme" load_steps=4 format=3 uid="uid://cwqlns34rj3vx"]
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bm5o2"]
|
|
||||||
content_margin_left = 4.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 4.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
bg_color = Color(0.1, 0.1, 0.1, 0.3)
|
|
||||||
border_width_left = 1
|
|
||||||
border_width_top = 1
|
|
||||||
border_width_right = 1
|
|
||||||
border_width_bottom = 1
|
|
||||||
border_color = Color(1, 1, 1, 0.27451)
|
|
||||||
corner_radius_top_left = 3
|
|
||||||
corner_radius_top_right = 3
|
|
||||||
corner_radius_bottom_right = 3
|
|
||||||
corner_radius_bottom_left = 3
|
|
||||||
corner_detail = 5
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l0k8a"]
|
|
||||||
content_margin_left = 4.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 4.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
bg_color = Color(0.225, 0.225, 0.225, 0.6)
|
|
||||||
border_width_left = 1
|
|
||||||
border_width_top = 1
|
|
||||||
border_width_right = 1
|
|
||||||
border_width_bottom = 1
|
|
||||||
border_color = Color(1, 1, 1, 0.784314)
|
|
||||||
corner_radius_top_left = 3
|
|
||||||
corner_radius_top_right = 3
|
|
||||||
corner_radius_bottom_right = 3
|
|
||||||
corner_radius_bottom_left = 3
|
|
||||||
corner_detail = 5
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1dkyv"]
|
|
||||||
content_margin_left = 4.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 4.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
bg_color = Color(0.1, 0.1, 0.1, 0.6)
|
|
||||||
border_width_left = 1
|
|
||||||
border_width_top = 1
|
|
||||||
border_width_right = 1
|
|
||||||
border_width_bottom = 1
|
|
||||||
border_color = Color(1, 1, 1, 0.509804)
|
|
||||||
corner_radius_top_left = 3
|
|
||||||
corner_radius_top_right = 3
|
|
||||||
corner_radius_bottom_right = 3
|
|
||||||
corner_radius_bottom_left = 3
|
|
||||||
corner_detail = 5
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
Button/styles/disabled = SubResource("StyleBoxFlat_bm5o2")
|
|
||||||
Button/styles/hover = SubResource("StyleBoxFlat_l0k8a")
|
|
||||||
Button/styles/normal = SubResource("StyleBoxFlat_1dkyv")
|
|
|
@ -1,7 +0,0 @@
|
||||||
extends SceneTree
|
|
||||||
|
|
||||||
# godot --headless --no-header -s tools/get_version.gd
|
|
||||||
|
|
||||||
func _init() -> void:
|
|
||||||
print(ProjectSettings.get_setting("application/config/version"))
|
|
||||||
quit(0)
|
|
|
@ -1 +0,0 @@
|
||||||
uid://cdhqbascy6pvy
|
|