initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

8
editor/shader/SCsub Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")
SConscript("shader_baker/SCsub")

View File

@@ -0,0 +1,160 @@
/**************************************************************************/
/* editor_native_shader_source_visualizer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "editor_native_shader_source_visualizer.h"
#include "editor/editor_string_names.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/code_edit.h"
#include "scene/gui/text_edit.h"
#include "servers/rendering/shader_language.h"
void EditorNativeShaderSourceVisualizer::_load_theme_settings() {
syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));
syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));
syntax_highlighter->clear_keyword_colors();
List<String> keywords;
ShaderLanguage::get_keyword_list(&keywords);
const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
for (const String &keyword : keywords) {
if (ShaderLanguage::is_control_flow_keyword(keyword)) {
syntax_highlighter->add_keyword_color(keyword, control_flow_keyword_color);
} else {
syntax_highlighter->add_keyword_color(keyword, keyword_color);
}
}
// Colorize comments.
const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
syntax_highlighter->clear_color_regions();
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
syntax_highlighter->add_color_region("//", "", comment_color, true);
// Colorize preprocessor statements.
const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
syntax_highlighter->add_color_region("#", "", user_type_color, true);
syntax_highlighter->set_uint_suffix_enabled(true);
}
void EditorNativeShaderSourceVisualizer::_inspect_shader(RID p_shader) {
if (versions) {
memdelete(versions);
versions = nullptr;
}
RS::ShaderNativeSourceCode nsc = RS::get_singleton()->shader_get_native_source_code(p_shader);
_load_theme_settings();
versions = memnew(TabContainer);
versions->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
versions->set_v_size_flags(Control::SIZE_EXPAND_FILL);
versions->set_h_size_flags(Control::SIZE_EXPAND_FILL);
for (int i = 0; i < nsc.versions.size(); i++) {
TabContainer *vtab = memnew(TabContainer);
vtab->set_name("Version " + itos(i));
vtab->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
vtab->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vtab->set_h_size_flags(Control::SIZE_EXPAND_FILL);
versions->add_child(vtab);
for (int j = 0; j < nsc.versions[i].stages.size(); j++) {
CodeEdit *code_edit = memnew(CodeEdit);
code_edit->set_editable(false);
code_edit->set_syntax_highlighter(syntax_highlighter);
code_edit->add_theme_font_override(SceneStringName(font), get_theme_font("source", EditorStringName(EditorFonts)));
code_edit->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size("source_size", EditorStringName(EditorFonts)));
code_edit->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
// Appearance: Caret
code_edit->set_caret_type((TextEdit::CaretType)EDITOR_GET("text_editor/appearance/caret/type").operator int());
code_edit->set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
code_edit->set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval"));
code_edit->set_highlight_current_line(EDITOR_GET("text_editor/appearance/caret/highlight_current_line"));
code_edit->set_highlight_all_occurrences(EDITOR_GET("text_editor/appearance/caret/highlight_all_occurrences"));
// Appearance: Gutters
code_edit->set_draw_line_numbers(EDITOR_GET("text_editor/appearance/gutters/show_line_numbers"));
code_edit->set_line_numbers_zero_padded(EDITOR_GET("text_editor/appearance/gutters/line_numbers_zero_padded"));
// Appearance: Minimap
code_edit->set_draw_minimap(EDITOR_GET("text_editor/appearance/minimap/show_minimap"));
code_edit->set_minimap_width((int)EDITOR_GET("text_editor/appearance/minimap/minimap_width") * EDSCALE);
// Appearance: Lines
code_edit->set_line_folding_enabled(EDITOR_GET("text_editor/appearance/lines/code_folding"));
code_edit->set_draw_fold_gutter(EDITOR_GET("text_editor/appearance/lines/code_folding"));
code_edit->set_line_wrapping_mode((TextEdit::LineWrappingMode)EDITOR_GET("text_editor/appearance/lines/word_wrap").operator int());
code_edit->set_autowrap_mode((TextServer::AutowrapMode)EDITOR_GET("text_editor/appearance/lines/autowrap_mode").operator int());
// Appearance: Whitespace
code_edit->set_draw_tabs(EDITOR_GET("text_editor/appearance/whitespace/draw_tabs"));
code_edit->set_draw_spaces(EDITOR_GET("text_editor/appearance/whitespace/draw_spaces"));
code_edit->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
// Behavior: Navigation
code_edit->set_scroll_past_end_of_file_enabled(EDITOR_GET("text_editor/behavior/navigation/scroll_past_end_of_file"));
code_edit->set_smooth_scroll_enabled(EDITOR_GET("text_editor/behavior/navigation/smooth_scrolling"));
code_edit->set_v_scroll_speed(EDITOR_GET("text_editor/behavior/navigation/v_scroll_speed"));
code_edit->set_drag_and_drop_selection_enabled(EDITOR_GET("text_editor/behavior/navigation/drag_and_drop_selection"));
// Behavior: Indent
code_edit->set_indent_size(EDITOR_GET("text_editor/behavior/indent/size"));
code_edit->set_auto_indent_enabled(EDITOR_GET("text_editor/behavior/indent/auto_indent"));
code_edit->set_indent_wrapped_lines(EDITOR_GET("text_editor/behavior/indent/indent_wrapped_lines"));
code_edit->set_name(nsc.versions[i].stages[j].name);
code_edit->set_text(nsc.versions[i].stages[j].code);
code_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
code_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vtab->add_child(code_edit);
}
}
add_child(versions);
popup_centered_ratio();
}
void EditorNativeShaderSourceVisualizer::_bind_methods() {
ClassDB::bind_method("_inspect_shader", &EditorNativeShaderSourceVisualizer::_inspect_shader);
}
EditorNativeShaderSourceVisualizer::EditorNativeShaderSourceVisualizer() {
syntax_highlighter.instantiate();
add_to_group("_native_shader_source_visualizer");
set_title(TTR("Native Shader Source Inspector"));
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* editor_native_shader_source_visualizer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
#include "scene/resources/syntax_highlighter.h"
class EditorNativeShaderSourceVisualizer : public AcceptDialog {
GDCLASS(EditorNativeShaderSourceVisualizer, AcceptDialog)
TabContainer *versions = nullptr;
Ref<CodeHighlighter> syntax_highlighter;
void _load_theme_settings();
void _inspect_shader(RID p_shader);
protected:
static void _bind_methods();
public:
EditorNativeShaderSourceVisualizer();
};

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
if env["vulkan"]:
env.add_source_files(env.editor_sources, "shader_baker_export_plugin_platform_vulkan.cpp")
if env["d3d12"]:
env.add_source_files(env.editor_sources, "shader_baker_export_plugin_platform_d3d12.cpp")
if env["metal"]:
env.add_source_files(env.editor_sources, "shader_baker_export_plugin_platform_metal.cpp")

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* shader_baker_export_plugin_platform_d3d12.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_baker_export_plugin_platform_d3d12.h"
#include "drivers/d3d12/rendering_shader_container_d3d12.h"
#include <windows.h>
RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformD3D12::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) {
if (lib_d3d12 == nullptr) {
lib_d3d12 = LoadLibraryW(L"D3D12.dll");
ERR_FAIL_NULL_V_MSG(lib_d3d12, nullptr, "Unable to load D3D12.dll.");
}
// Shader Model 6.2 is required to export shaders that have FP16 variants.
RenderingShaderContainerFormatD3D12 *shader_container_format_d3d12 = memnew(RenderingShaderContainerFormatD3D12);
shader_container_format_d3d12->set_lib_d3d12(lib_d3d12);
return shader_container_format_d3d12;
}
bool ShaderBakerExportPluginPlatformD3D12::matches_driver(const String &p_driver) {
return p_driver == "d3d12";
}
ShaderBakerExportPluginPlatformD3D12 ::~ShaderBakerExportPluginPlatformD3D12() {
if (lib_d3d12 != nullptr) {
FreeLibrary((HMODULE)(lib_d3d12));
}
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* shader_baker_export_plugin_platform_d3d12.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/export/shader_baker_export_plugin.h"
class ShaderBakerExportPluginPlatformD3D12 : public ShaderBakerExportPluginPlatform {
GDCLASS(ShaderBakerExportPluginPlatformD3D12, ShaderBakerExportPluginPlatform);
private:
void *lib_d3d12 = nullptr;
public:
virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) override;
virtual bool matches_driver(const String &p_driver) override;
virtual ~ShaderBakerExportPluginPlatformD3D12() override;
};

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* shader_baker_export_plugin_platform_metal.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_baker_export_plugin_platform_metal.h"
#include "drivers/metal/rendering_shader_container_metal.h"
RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformMetal::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) {
const String &os_name = p_platform->get_os_name();
const MetalDeviceProfile *profile;
String min_os_version;
if (os_name == U"macOS") {
profile = MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform::macOS, MetalDeviceProfile::GPU::Apple7);
// Godot metal doesn't support x86_64 mac so no need to worry about that version
min_os_version = p_preset->get("application/min_macos_version_arm64");
} else if (os_name == U"iOS") {
profile = MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform::iOS, MetalDeviceProfile::GPU::Apple7);
min_os_version = p_preset->get("application/min_ios_version");
} else {
ERR_FAIL_V_MSG(nullptr, vformat("Unsupported platform: %s", os_name));
}
return memnew(RenderingShaderContainerFormatMetal(profile, true, min_os_version));
}
bool ShaderBakerExportPluginPlatformMetal::matches_driver(const String &p_driver) {
return p_driver == "metal";
}

View File

@@ -0,0 +1,39 @@
/**************************************************************************/
/* shader_baker_export_plugin_platform_metal.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/export/shader_baker_export_plugin.h"
class ShaderBakerExportPluginPlatformMetal : public ShaderBakerExportPluginPlatform {
public:
virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) override;
virtual bool matches_driver(const String &p_driver) override;
};

View File

@@ -0,0 +1,41 @@
/**************************************************************************/
/* shader_baker_export_plugin_platform_vulkan.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_baker_export_plugin_platform_vulkan.h"
#include "drivers/vulkan/rendering_shader_container_vulkan.h"
RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformVulkan::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) {
return memnew(RenderingShaderContainerFormatVulkan);
}
bool ShaderBakerExportPluginPlatformVulkan::matches_driver(const String &p_driver) {
return p_driver == "vulkan";
}

View File

@@ -0,0 +1,41 @@
/**************************************************************************/
/* shader_baker_export_plugin_platform_vulkan.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/export/shader_baker_export_plugin.h"
class ShaderBakerExportPluginPlatformVulkan : public ShaderBakerExportPluginPlatform {
GDCLASS(ShaderBakerExportPluginPlatformVulkan, ShaderBakerExportPluginPlatform);
public:
virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) override;
virtual bool matches_driver(const String &p_driver) override;
};

View File

@@ -0,0 +1,687 @@
/**************************************************************************/
/* shader_create_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_create_dialog.h"
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_validation_panel.h"
#include "editor/themes/editor_scale.h"
#include "scene/resources/shader_include.h"
#include "scene/resources/visual_shader.h"
#include "servers/rendering/shader_types.h"
enum ShaderType {
SHADER_TYPE_TEXT,
SHADER_TYPE_VISUAL,
SHADER_TYPE_INC,
SHADER_TYPE_MAX,
};
void ShaderCreateDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
String last_lang = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_language", "");
if (!last_lang.is_empty()) {
for (int i = 0; i < type_menu->get_item_count(); i++) {
if (type_menu->get_item_text(i) == last_lang) {
type_menu->select(i);
current_type = i;
break;
}
}
} else {
type_menu->select(default_type);
}
current_mode = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_mode", 0);
mode_menu->select(current_mode);
} break;
case NOTIFICATION_THEME_CHANGED: {
static const char *shader_types[3] = { "Shader", "VisualShader", "TextFile" };
for (int i = 0; i < 3; i++) {
Ref<Texture2D> icon = get_editor_theme_icon(shader_types[i]);
if (icon.is_valid()) {
type_menu->set_item_icon(i, icon);
}
}
path_button->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
} break;
}
}
void ShaderCreateDialog::_update_language_info() {
type_data.clear();
for (int i = 0; i < SHADER_TYPE_MAX; i++) {
ShaderTypeData shader_type_data;
if (i == int(SHADER_TYPE_TEXT)) {
shader_type_data.use_templates = true;
shader_type_data.extensions.push_back("gdshader");
shader_type_data.default_extension = "gdshader";
} else if (i == int(SHADER_TYPE_INC)) {
shader_type_data.extensions.push_back("gdshaderinc");
shader_type_data.default_extension = "gdshaderinc";
} else {
shader_type_data.default_extension = "tres";
}
shader_type_data.extensions.push_back("res");
shader_type_data.extensions.push_back("tres");
type_data.push_back(shader_type_data);
}
}
void ShaderCreateDialog::_path_hbox_sorted() {
if (is_visible()) {
int filename_start_pos = initial_base_path.rfind_char('/') + 1;
int filename_end_pos = initial_base_path.length();
if (!is_built_in) {
file_path->select(filename_start_pos, filename_end_pos);
}
file_path->set_caret_column(file_path->get_text().length());
file_path->set_caret_column(filename_start_pos);
file_path->grab_focus();
}
}
void ShaderCreateDialog::_mode_changed(int p_mode) {
current_mode = p_mode;
EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_mode", p_mode);
}
void ShaderCreateDialog::_template_changed(int p_template) {
current_template = p_template;
EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_template", p_template);
}
void ShaderCreateDialog::ok_pressed() {
if (is_new_shader_created) {
_create_new();
if (built_in_enabled) {
// Only save state of built-in checkbox if it's enabled.
EditorSettings::get_singleton()->set_project_metadata("shader_setup", "create_built_in_shader", internal->is_pressed());
}
} else {
_load_exist();
}
is_new_shader_created = true;
validation_panel->update();
}
void ShaderCreateDialog::_create_new() {
Ref<Resource> shader;
Ref<Resource> shader_inc;
switch (type_menu->get_selected()) {
case SHADER_TYPE_TEXT: {
Ref<Shader> text_shader;
text_shader.instantiate();
shader = text_shader;
StringBuilder code;
code += vformat("shader_type %s;\n", mode_menu->get_text().to_snake_case());
if (current_template == 0) { // Default template.
switch (current_mode) {
case Shader::MODE_SPATIAL:
code += R"(
void vertex() {
// Called for every vertex the material is visible on.
}
void fragment() {
// Called for every pixel the material is visible on.
}
//void light() {
// // Called for every pixel for every light affecting the material.
// // Uncomment to replace the default light processing function with this one.
//}
)";
break;
case Shader::MODE_CANVAS_ITEM:
code += R"(
void vertex() {
// Called for every vertex the material is visible on.
}
void fragment() {
// Called for every pixel the material is visible on.
}
//void light() {
// // Called for every pixel for every light affecting the CanvasItem.
// // Uncomment to replace the default light processing function with this one.
//}
)";
break;
case Shader::MODE_PARTICLES:
code += R"(
void start() {
// Called when a particle is spawned.
}
void process() {
// Called every frame on existing particles (according to the Fixed FPS property).
}
)";
break;
case Shader::MODE_SKY:
code += R"(
void sky() {
// Called for every visible pixel in the sky background, as well as all pixels
// in the radiance cubemap.
}
)";
break;
case Shader::MODE_FOG:
code += R"(
void fog() {
// Called once for every froxel that is touched by an axis-aligned bounding box
// of the associated FogVolume. This means that froxels that just barely touch
// a given FogVolume will still be used.
}
)";
}
}
text_shader->set_code(code.as_string());
} break;
case SHADER_TYPE_VISUAL: {
Ref<VisualShader> visual_shader;
visual_shader.instantiate();
shader = visual_shader;
visual_shader->set_mode(Shader::Mode(current_mode));
} break;
case SHADER_TYPE_INC: {
Ref<ShaderInclude> include;
include.instantiate();
shader_inc = include;
} break;
default: {
} break;
}
if (shader.is_null()) {
String lpath = ProjectSettings::get_singleton()->localize_path(file_path->get_text());
shader_inc->set_path(lpath);
Error error = ResourceSaver::save(shader_inc, lpath, ResourceSaver::FLAG_CHANGE_PATH);
if (error != OK) {
alert->set_text(TTR("Error - Could not create shader include in filesystem."));
alert->popup_centered();
return;
}
emit_signal(SNAME("shader_include_created"), shader_inc);
} else {
if (is_built_in) {
Node *edited_scene = get_tree()->get_edited_scene_root();
if (likely(edited_scene)) {
shader->set_path(edited_scene->get_scene_file_path() + "::" + shader->generate_scene_unique_id());
}
} else {
String lpath = ProjectSettings::get_singleton()->localize_path(file_path->get_text());
shader->set_path(lpath);
Error error = ResourceSaver::save(shader, lpath, ResourceSaver::FLAG_CHANGE_PATH);
if (error != OK) {
alert->set_text(TTR("Error - Could not create shader in filesystem."));
alert->popup_centered();
return;
}
}
emit_signal(SNAME("shader_created"), shader);
}
file_path->set_text(file_path->get_text().get_base_dir());
hide();
}
void ShaderCreateDialog::_load_exist() {
String path = file_path->get_text();
Ref<Resource> p_shader = ResourceLoader::load(path, "Shader");
if (p_shader.is_null()) {
alert->set_text(vformat(TTR("Error loading shader from %s"), path));
alert->popup_centered();
return;
}
emit_signal(SNAME("shader_created"), p_shader);
hide();
}
void ShaderCreateDialog::_type_changed(int p_language) {
current_type = p_language;
ShaderTypeData shader_type_data = type_data.get(p_language);
String selected_ext = "." + shader_type_data.default_extension;
String path = file_path->get_text();
String extension = "";
if (!path.is_empty()) {
if (path.contains_char('.')) {
extension = path.get_extension();
}
if (extension.length() == 0) {
path += selected_ext;
} else {
path = path.get_basename() + selected_ext;
}
} else {
path = "shader" + selected_ext;
}
_path_changed(path);
file_path->set_text(path);
type_menu->set_item_disabled(int(SHADER_TYPE_INC), load_enabled);
mode_menu->set_disabled(p_language == SHADER_TYPE_INC);
template_menu->set_disabled(!shader_type_data.use_templates);
template_menu->clear();
if (shader_type_data.use_templates) {
int last_template = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_template", 0);
template_menu->add_item(TTRC("Default"));
template_menu->add_item(TTRC("Empty"));
template_menu->select(last_template);
current_template = last_template;
} else {
template_menu->add_item(TTRC("N/A"));
}
EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", type_menu->get_item_text(type_menu->get_selected()));
validation_panel->update();
}
void ShaderCreateDialog::_built_in_toggled(bool p_enabled) {
is_built_in = p_enabled;
if (p_enabled) {
is_new_shader_created = true;
} else {
_path_changed(file_path->get_text());
}
validation_panel->update();
}
void ShaderCreateDialog::_browse_path() {
file_browse->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_browse->set_title(TTR("Open Shader / Choose Location"));
file_browse->set_ok_button_text(TTR("Open"));
file_browse->set_disable_overwrite_warning(true);
file_browse->clear_filters();
List<String> extensions = type_data.get(type_menu->get_selected()).extensions;
for (const String &E : extensions) {
file_browse->add_filter("*." + E);
}
file_browse->set_current_path(file_path->get_text());
file_browse->popup_file_dialog();
}
void ShaderCreateDialog::_file_selected(const String &p_file) {
String p = ProjectSettings::get_singleton()->localize_path(p_file);
file_path->set_text(p);
_path_changed(p);
String filename = p.get_file().get_basename();
int select_start = p.rfind(filename);
file_path->select(select_start, select_start + filename.length());
file_path->set_caret_column(select_start + filename.length());
file_path->grab_focus();
}
void ShaderCreateDialog::_path_changed(const String &p_path) {
if (is_built_in) {
return;
}
is_path_valid = false;
is_new_shader_created = true;
path_error = _validate_path(p_path);
if (!path_error.is_empty()) {
validation_panel->update();
return;
}
Ref<DirAccess> f = DirAccess::create(DirAccess::ACCESS_RESOURCES);
String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges());
if (f->file_exists(p)) {
is_new_shader_created = false;
}
is_path_valid = true;
validation_panel->update();
}
void ShaderCreateDialog::_path_submitted(const String &p_path) {
if (!get_ok_button()->is_disabled()) {
ok_pressed();
}
}
void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled, int p_preferred_type, int p_preferred_mode) {
if (!p_base_path.is_empty()) {
initial_base_path = p_base_path.get_basename();
file_path->set_text(initial_base_path + "." + type_data.get(type_menu->get_selected()).default_extension);
current_type = type_menu->get_selected();
} else {
initial_base_path = "";
file_path->set_text("");
}
file_path->deselect();
built_in_enabled = p_built_in_enabled;
load_enabled = p_load_enabled;
if (built_in_enabled) {
internal->set_pressed(EditorSettings::get_singleton()->get_project_metadata("shader_setup", "create_built_in_shader", false));
}
if (p_preferred_type > -1) {
type_menu->select(p_preferred_type);
_type_changed(p_preferred_type);
}
if (p_preferred_mode > -1) {
mode_menu->select(p_preferred_mode);
_mode_changed(p_preferred_mode);
}
_type_changed(current_type);
_path_changed(file_path->get_text());
}
String ShaderCreateDialog::_validate_path(const String &p_path) {
String p = p_path.strip_edges();
if (p.is_empty()) {
return TTR("Path is empty.");
}
if (p.get_file().get_basename().is_empty()) {
return TTR("Filename is empty.");
}
p = ProjectSettings::get_singleton()->localize_path(p);
if (!p.begins_with("res://")) {
return TTR("Path is not local.");
}
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (d->change_dir(p.get_base_dir()) != OK) {
return TTR("Invalid base path.");
}
Ref<DirAccess> f = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (f->dir_exists(p)) {
return TTR("A directory with the same name exists.");
}
String extension = p.get_extension();
HashSet<String> extensions;
List<ShaderCreateDialog::ShaderTypeData>::ConstIterator itr = type_data.begin();
for (int i = 0; i < SHADER_TYPE_MAX; ++itr, ++i) {
for (const String &ext : itr->extensions) {
if (!extensions.has(ext)) {
extensions.insert(ext);
}
}
}
bool found = false;
bool match = false;
for (const String &ext : extensions) {
if (ext.nocasecmp_to(extension) == 0) {
found = true;
for (const String &type_ext : type_data.get(current_type).extensions) {
if (type_ext.nocasecmp_to(extension) == 0) {
match = true;
break;
}
}
break;
}
}
if (!found) {
return TTR("Invalid extension.");
}
if (!match) {
return TTR("Wrong extension chosen.");
}
return "";
}
void ShaderCreateDialog::_update_dialog() {
if (!is_built_in && !is_path_valid) {
validation_panel->set_message(MSG_ID_SHADER, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR);
}
if (!is_built_in && !path_error.is_empty()) {
validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR);
} else if (validation_panel->is_valid() && !is_new_shader_created) {
validation_panel->set_message(MSG_ID_SHADER, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK);
}
if (!built_in_enabled) {
internal->set_pressed(false);
}
if (is_built_in) {
file_path->set_editable(false);
path_button->set_disabled(true);
re_check_path = true;
} else {
file_path->set_editable(true);
path_button->set_disabled(false);
if (re_check_path) {
re_check_path = false;
_path_changed(file_path->get_text());
}
}
internal->set_disabled(!built_in_enabled);
if (is_built_in) {
validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in shaders can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false);
}
if (is_built_in) {
set_ok_button_text(TTR("Create"));
validation_panel->set_message(MSG_ID_PATH, TTR("Built-in shader (into scene file)."), EditorValidationPanel::MSG_OK);
} else if (is_new_shader_created) {
set_ok_button_text(TTR("Create"));
} else if (load_enabled) {
set_ok_button_text(TTR("Load"));
if (is_path_valid) {
validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing shader file."), EditorValidationPanel::MSG_OK);
}
} else {
set_ok_button_text(TTR("Create"));
validation_panel->set_message(MSG_ID_PATH, TTR("Shader file already exists."), EditorValidationPanel::MSG_ERROR);
}
}
void ShaderCreateDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("config", "path", "built_in_enabled", "load_enabled"), &ShaderCreateDialog::config, DEFVAL(true), DEFVAL(true));
ADD_SIGNAL(MethodInfo("shader_created", PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader")));
ADD_SIGNAL(MethodInfo("shader_include_created", PropertyInfo(Variant::OBJECT, "shader_include", PROPERTY_HINT_RESOURCE_TYPE, "ShaderInclude")));
}
ShaderCreateDialog::ShaderCreateDialog() {
_update_language_info();
// Main Controls.
gc = memnew(GridContainer);
gc->set_columns(2);
// Error Fields.
validation_panel = memnew(EditorValidationPanel);
validation_panel->add_line(MSG_ID_SHADER, TTR("Shader path/name is valid."));
validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new shader file."));
validation_panel->add_line(MSG_ID_BUILT_IN);
validation_panel->set_update_callback(callable_mp(this, &ShaderCreateDialog::_update_dialog));
validation_panel->set_accept_button(get_ok_button());
// Spacing.
Control *spacing = memnew(Control);
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
VBoxContainer *vb = memnew(VBoxContainer);
vb->add_child(gc);
vb->add_child(spacing);
vb->add_child(validation_panel);
add_child(vb);
// Type.
type_menu = memnew(OptionButton);
type_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
type_menu->set_accessibility_name(TTRC("Type:"));
type_menu->set_custom_minimum_size(Size2(250, 0) * EDSCALE);
type_menu->set_h_size_flags(Control::SIZE_EXPAND_FILL);
gc->add_child(memnew(Label(TTR("Type:"))));
gc->add_child(type_menu);
for (int i = 0; i < SHADER_TYPE_MAX; i++) {
String type;
bool invalid = false;
switch (i) {
case SHADER_TYPE_TEXT:
type = "Shader";
default_type = i;
break;
case SHADER_TYPE_VISUAL:
type = "VisualShader";
break;
case SHADER_TYPE_INC:
type = "ShaderInclude";
break;
case SHADER_TYPE_MAX:
invalid = true;
break;
default:
invalid = true;
break;
}
if (invalid) {
continue;
}
type_menu->add_item(type);
}
if (default_type >= 0) {
type_menu->select(default_type);
}
current_type = default_type;
type_menu->connect(SceneStringName(item_selected), callable_mp(this, &ShaderCreateDialog::_type_changed));
// Modes.
mode_menu = memnew(OptionButton);
mode_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
mode_menu->set_accessibility_name(TTRC("Mode:"));
for (const String &type_name : ShaderTypes::get_singleton()->get_types_list()) {
mode_menu->add_item(type_name.capitalize());
}
gc->add_child(memnew(Label(TTR("Mode:"))));
gc->add_child(mode_menu);
mode_menu->connect(SceneStringName(item_selected), callable_mp(this, &ShaderCreateDialog::_mode_changed));
// Templates.
template_menu = memnew(OptionButton);
template_menu->set_accessibility_name(TTRC("Template:"));
gc->add_child(memnew(Label(TTR("Template:"))));
gc->add_child(template_menu);
template_menu->connect(SceneStringName(item_selected), callable_mp(this, &ShaderCreateDialog::_template_changed));
// Built-in Shader.
internal = memnew(CheckBox);
internal->set_text(TTR("On"));
internal->set_accessibility_name(TTRC("Built-in Shader:"));
internal->connect(SceneStringName(toggled), callable_mp(this, &ShaderCreateDialog::_built_in_toggled));
gc->add_child(memnew(Label(TTR("Built-in Shader:"))));
gc->add_child(internal);
// Path.
HBoxContainer *hb = memnew(HBoxContainer);
hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hb->connect(SceneStringName(sort_children), callable_mp(this, &ShaderCreateDialog::_path_hbox_sorted));
file_path = memnew(LineEdit);
file_path->connect(SceneStringName(text_changed), callable_mp(this, &ShaderCreateDialog::_path_changed));
file_path->set_accessibility_name(TTRC("Path:"));
file_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hb->add_child(file_path);
register_text_enter(file_path);
path_button = memnew(Button);
path_button->set_accessibility_name(TTRC("Select"));
path_button->connect(SceneStringName(pressed), callable_mp(this, &ShaderCreateDialog::_browse_path));
hb->add_child(path_button);
gc->add_child(memnew(Label(TTR("Path:"))));
gc->add_child(hb);
// Dialog Setup.
file_browse = memnew(EditorFileDialog);
file_browse->connect("file_selected", callable_mp(this, &ShaderCreateDialog::_file_selected));
file_browse->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
add_child(file_browse);
alert = memnew(AcceptDialog);
alert->get_label()->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
alert->get_label()->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
alert->get_label()->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
alert->get_label()->set_custom_minimum_size(Size2(325, 60) * EDSCALE);
add_child(alert);
set_ok_button_text(TTR("Create"));
set_hide_on_ok(false);
set_title(TTR("Create Shader"));
}

View File

@@ -0,0 +1,109 @@
/**************************************************************************/
/* shader_create_dialog.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/settings/editor_settings.h"
#include "scene/gui/check_box.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
class EditorFileDialog;
class EditorValidationPanel;
class ShaderCreateDialog : public ConfirmationDialog {
GDCLASS(ShaderCreateDialog, ConfirmationDialog);
enum {
MSG_ID_SHADER,
MSG_ID_PATH,
MSG_ID_BUILT_IN,
};
struct ShaderTypeData {
List<String> extensions;
String default_extension;
bool use_templates = false;
};
List<ShaderTypeData> type_data;
GridContainer *gc = nullptr;
EditorValidationPanel *validation_panel = nullptr;
OptionButton *type_menu = nullptr;
OptionButton *mode_menu = nullptr;
OptionButton *template_menu = nullptr;
CheckBox *internal = nullptr;
LineEdit *file_path = nullptr;
Button *path_button = nullptr;
EditorFileDialog *file_browse = nullptr;
AcceptDialog *alert = nullptr;
String initial_base_path;
String path_error;
bool is_new_shader_created = true;
bool is_path_valid = false;
bool is_built_in = false;
bool built_in_enabled = true;
bool load_enabled = false;
bool re_check_path = false;
int current_type = -1;
int default_type = -1;
int current_mode = 0;
int current_template = 0;
virtual void _update_language_info();
void _path_hbox_sorted();
void _path_changed(const String &p_path = String());
void _path_submitted(const String &p_path = String());
void _type_changed(int p_type = 0);
void _built_in_toggled(bool p_enabled);
void _template_changed(int p_template = 0);
void _mode_changed(int p_mode = 0);
void _browse_path();
void _file_selected(const String &p_file);
String _validate_path(const String &p_path);
virtual void ok_pressed() override;
void _create_new();
void _load_exist();
void _update_dialog();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void config(const String &p_base_path, bool p_built_in_enabled = true, bool p_load_enabled = true, int p_preferred_type = -1, int p_preferred_mode = -1);
ShaderCreateDialog();
};

View File

@@ -0,0 +1,51 @@
/**************************************************************************/
/* shader_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "scene/gui/control.h"
#include "scene/resources/shader.h"
class Button;
class MenuButton;
class ShaderEditor : public Control {
GDCLASS(ShaderEditor, Control);
public:
virtual void edit_shader(const Ref<Shader> &p_shader) = 0;
virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) {}
virtual void use_menu_bar_items(MenuButton *p_file_menu, Button *p_make_floating) = 0;
virtual void apply_shaders() = 0;
virtual bool is_unsaved() const = 0;
virtual void save_external_data(const String &p_str = "") = 0;
virtual void validate_script() = 0;
};

View File

@@ -0,0 +1,919 @@
/**************************************************************************/
/* shader_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_editor_plugin.h"
#include "editor/docks/filesystem_dock.h"
#include "editor/docks/inspector_dock.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/window_wrapper.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/shader/shader_create_dialog.h"
#include "editor/shader/text_shader_editor.h"
#include "editor/shader/visual_shader_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/item_list.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/texture_rect.h"
Ref<Resource> ShaderEditorPlugin::_get_current_shader() {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX_V(index, shader_tabs->get_tab_count(), Ref<Resource>());
if (edited_shaders[index].shader.is_valid()) {
return edited_shaders[index].shader;
} else {
return edited_shaders[index].shader_inc;
}
}
void ShaderEditorPlugin::_update_shader_list() {
shader_list->clear();
for (EditedShader &edited_shader : edited_shaders) {
Ref<Resource> shader = edited_shader.shader;
if (shader.is_null()) {
shader = edited_shader.shader_inc;
}
String path = shader->get_path();
String text = path.get_file();
if (text.is_empty()) {
// This appears for newly created built-in shaders before saving the scene.
text = TTR("[unsaved]");
} else if (shader->is_built_in()) {
const String &shader_name = shader->get_name();
if (!shader_name.is_empty()) {
text = vformat("%s (%s)", shader_name, text.get_slice("::", 0));
}
}
// When shader is deleted in filesystem dock, need this to correctly close shader editor.
edited_shader.path = path;
bool unsaved = false;
if (edited_shader.shader_editor) {
unsaved = edited_shader.shader_editor->is_unsaved();
}
// TODO: Handle visual shaders too.
if (unsaved) {
text += "(*)";
}
String _class = shader->get_class();
if (!shader_list->has_theme_icon(_class, EditorStringName(EditorIcons))) {
_class = "TextFile";
}
Ref<Texture2D> icon = shader_list->get_editor_theme_icon(_class);
shader_list->add_item(text, icon);
shader_list->set_item_tooltip(-1, path);
edited_shader.name = text;
}
if (shader_tabs->get_tab_count()) {
shader_list->select(shader_tabs->get_current_tab());
}
_set_file_specific_items_disabled(edited_shaders.is_empty());
_update_shader_list_status();
}
void ShaderEditorPlugin::_update_shader_list_status() {
for (int i = 0; i < shader_list->get_item_count(); i++) {
TextShaderEditor *se = Object::cast_to<TextShaderEditor>(shader_tabs->get_tab_control(i));
if (se) {
if (se->was_compilation_successful()) {
shader_list->set_item_tag_icon(i, Ref<Texture2D>());
} else {
shader_list->set_item_tag_icon(i, shader_list->get_editor_theme_icon(SNAME("Error")));
}
}
}
}
void ShaderEditorPlugin::_move_shader_tab(int p_from, int p_to) {
if (p_from == p_to) {
return;
}
EditedShader es = edited_shaders[p_from];
edited_shaders.remove_at(p_from);
edited_shaders.insert(p_to, es);
shader_tabs->move_child(shader_tabs->get_tab_control(p_from), p_to);
_update_shader_list();
}
void ShaderEditorPlugin::edit(Object *p_object) {
if (!p_object) {
return;
}
EditedShader es;
ShaderInclude *si = Object::cast_to<ShaderInclude>(p_object);
if (si != nullptr) {
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
if (edited_shaders[i].shader_inc.ptr() == si) {
shader_tabs->set_current_tab(i);
shader_list->select(i);
_switch_to_editor(edited_shaders[i].shader_editor);
return;
}
}
es.shader_inc = Ref<ShaderInclude>(si);
TextShaderEditor *text_shader = memnew(TextShaderEditor);
text_shader->get_code_editor()->set_toggle_list_control(shader_list);
es.shader_editor = text_shader;
es.shader_editor->edit_shader_include(si);
shader_tabs->add_child(es.shader_editor);
} else {
Shader *s = Object::cast_to<Shader>(p_object);
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
if (edited_shaders[i].shader.ptr() == s) {
shader_tabs->set_current_tab(i);
shader_list->select(i);
_switch_to_editor(edited_shaders[i].shader_editor);
return;
}
}
es.shader = Ref<Shader>(s);
Ref<VisualShader> vs = es.shader;
if (vs.is_valid()) {
VisualShaderEditor *vs_editor = memnew(VisualShaderEditor);
vs_editor->set_toggle_list_control(shader_list);
es.shader_editor = vs_editor;
} else {
TextShaderEditor *text_shader = memnew(TextShaderEditor);
text_shader->get_code_editor()->set_toggle_list_control(shader_list);
es.shader_editor = text_shader;
}
shader_tabs->add_child(es.shader_editor);
es.shader_editor->edit_shader(es.shader);
}
TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(es.shader_editor);
if (text_shader_editor) {
text_shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
CodeTextEditor *cte = text_shader_editor->get_code_editor();
if (cte) {
cte->set_zoom_factor(text_shader_zoom_factor);
cte->connect("zoomed", callable_mp(this, &ShaderEditorPlugin::_set_text_shader_zoom_factor));
cte->connect(SceneStringName(visibility_changed), callable_mp(this, &ShaderEditorPlugin::_update_shader_editor_zoom_factor).bind(cte));
}
}
shader_tabs->set_current_tab(shader_tabs->get_tab_count() - 1);
edited_shaders.push_back(es);
_update_shader_list();
_switch_to_editor(es.shader_editor);
}
bool ShaderEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<Shader>(p_object) != nullptr || Object::cast_to<ShaderInclude>(p_object) != nullptr;
}
void ShaderEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
EditorNode::get_bottom_panel()->make_item_visible(window_wrapper);
}
}
void ShaderEditorPlugin::selected_notify() {
}
ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader == p_for_shader) {
return edited_shader.shader_editor;
}
}
return nullptr;
}
void ShaderEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
if (EDITOR_GET("interface/multi_window/restore_windows_on_load") && window_wrapper->is_window_available() && p_layout->has_section_key("ShaderEditor", "window_rect")) {
window_wrapper->restore_window_from_saved_position(
p_layout->get_value("ShaderEditor", "window_rect", Rect2i()),
p_layout->get_value("ShaderEditor", "window_screen", -1),
p_layout->get_value("ShaderEditor", "window_screen_rect", Rect2i()));
} else {
window_wrapper->set_window_enabled(false);
}
if (!bool(EDITOR_GET("editors/shader_editor/behavior/files/restore_shaders_on_load"))) {
return;
}
if (!p_layout->has_section("ShaderEditor")) {
return;
}
if (!p_layout->has_section_key("ShaderEditor", "open_shaders") ||
!p_layout->has_section_key("ShaderEditor", "selected_shader")) {
return;
}
Array shaders = p_layout->get_value("ShaderEditor", "open_shaders");
int selected_shader_idx = 0;
String selected_shader = p_layout->get_value("ShaderEditor", "selected_shader");
for (int i = 0; i < shaders.size(); i++) {
String path = shaders[i];
Ref<Resource> res = ResourceLoader::load(path);
if (res.is_valid()) {
edit(res.ptr());
}
if (selected_shader == path) {
selected_shader_idx = i;
}
}
if (p_layout->has_section_key("ShaderEditor", "split_offset")) {
files_split->set_split_offset(p_layout->get_value("ShaderEditor", "split_offset"));
}
_update_shader_list();
_shader_selected(selected_shader_idx, false);
_set_text_shader_zoom_factor(p_layout->get_value("ShaderEditor", "text_shader_zoom_factor", 1.0f));
}
void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
if (window_wrapper->get_window_enabled()) {
p_layout->set_value("ShaderEditor", "window_rect", window_wrapper->get_window_rect());
int screen = window_wrapper->get_window_screen();
p_layout->set_value("ShaderEditor", "window_screen", screen);
p_layout->set_value("ShaderEditor", "window_screen_rect", DisplayServer::get_singleton()->screen_get_usable_rect(screen));
} else {
if (p_layout->has_section_key("ShaderEditor", "window_rect")) {
p_layout->erase_section_key("ShaderEditor", "window_rect");
}
if (p_layout->has_section_key("ShaderEditor", "window_screen")) {
p_layout->erase_section_key("ShaderEditor", "window_screen");
}
if (p_layout->has_section_key("ShaderEditor", "window_screen_rect")) {
p_layout->erase_section_key("ShaderEditor", "window_screen_rect");
}
}
Array shaders;
String selected_shader;
for (int i = 0; i < shader_tabs->get_tab_count(); i++) {
EditedShader edited_shader = edited_shaders[i];
if (edited_shader.shader_editor) {
String shader_path;
if (edited_shader.shader.is_valid()) {
shader_path = edited_shader.shader->get_path();
} else {
DEV_ASSERT(edited_shader.shader_inc.is_valid());
shader_path = edited_shader.shader_inc->get_path();
}
shaders.push_back(shader_path);
ShaderEditor *shader_editor = Object::cast_to<ShaderEditor>(shader_tabs->get_current_tab_control());
if (shader_editor && edited_shader.shader_editor == shader_editor) {
selected_shader = shader_path;
}
}
}
p_layout->set_value("ShaderEditor", "open_shaders", shaders);
p_layout->set_value("ShaderEditor", "split_offset", files_split->get_split_offset());
p_layout->set_value("ShaderEditor", "selected_shader", selected_shader);
p_layout->set_value("ShaderEditor", "text_shader_zoom_factor", text_shader_zoom_factor);
}
String ShaderEditorPlugin::get_unsaved_status(const String &p_for_scene) const {
// TODO: This should also include visual shaders and shader includes, but save_external_data() doesn't seem to save them...
PackedStringArray unsaved_shaders;
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
if (edited_shaders[i].shader_editor) {
if (edited_shaders[i].shader_editor->is_unsaved()) {
if (unsaved_shaders.is_empty()) {
unsaved_shaders.append(TTR("Save changes to the following shaders(s) before quitting?"));
}
unsaved_shaders.append(edited_shaders[i].name.trim_suffix("(*)"));
}
}
}
if (!p_for_scene.is_empty()) {
PackedStringArray unsaved_built_in_shaders;
const String scene_file = p_for_scene.get_file();
for (const String &E : unsaved_shaders) {
if (!E.is_resource_file() && E.contains(scene_file)) {
if (unsaved_built_in_shaders.is_empty()) {
unsaved_built_in_shaders.append(TTR("There are unsaved changes in the following built-in shaders(s):"));
}
unsaved_built_in_shaders.append(E);
}
}
if (!unsaved_built_in_shaders.is_empty()) {
return String("\n").join(unsaved_built_in_shaders);
}
return String();
}
return String("\n").join(unsaved_shaders);
}
void ShaderEditorPlugin::save_external_data() {
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader_editor && edited_shader.shader_editor->is_unsaved()) {
edited_shader.shader_editor->save_external_data();
}
}
_update_shader_list();
}
void ShaderEditorPlugin::apply_changes() {
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader_editor) {
edited_shader.shader_editor->apply_shaders();
}
}
}
void ShaderEditorPlugin::_shader_selected(int p_index, bool p_push_item) {
if (p_index >= (int)edited_shaders.size()) {
return;
}
if (edited_shaders[p_index].shader_editor) {
_switch_to_editor(edited_shaders[p_index].shader_editor);
edited_shaders[p_index].shader_editor->validate_script();
}
shader_tabs->set_current_tab(p_index);
shader_list->select(p_index);
if (p_push_item) {
// Avoid `Shader` being edited when editing `ShaderInclude` due to inspector refreshing.
if (edited_shaders[p_index].shader.is_valid()) {
EditorNode::get_singleton()->push_item_no_inspector(edited_shaders[p_index].shader.ptr());
} else {
EditorNode::get_singleton()->push_item_no_inspector(edited_shaders[p_index].shader_inc.ptr());
}
}
}
void ShaderEditorPlugin::_shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index == MouseButton::MIDDLE) {
_close_shader(p_item);
}
if (p_mouse_button_index == MouseButton::RIGHT) {
_make_script_list_context_menu();
}
}
void ShaderEditorPlugin::_setup_popup_menu(PopupMenuType p_type, PopupMenu *p_menu) {
if (p_type == FILE) {
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/new", TTRC("New Shader..."), KeyModifierMask::CMD_OR_CTRL | Key::N), FILE_MENU_NEW);
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/new_include", TTRC("New Shader Include..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_MENU_NEW_INCLUDE);
p_menu->add_separator();
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/open", TTRC("Load Shader File..."), KeyModifierMask::CMD_OR_CTRL | Key::O), FILE_MENU_OPEN);
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/open_include", TTRC("Load Shader Include File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::O), FILE_MENU_OPEN_INCLUDE);
}
if (p_type == FILE || p_type == CONTEXT_VALID_ITEM) {
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/save", TTRC("Save File"), KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::S), FILE_MENU_SAVE);
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/save_as", TTRC("Save File As...")), FILE_MENU_SAVE_AS);
}
if (p_type == FILE) {
p_menu->add_separator();
p_menu->add_item(TTR("Open File in Inspector"), FILE_MENU_INSPECT);
p_menu->add_item(TTR("Inspect Native Shader Code..."), FILE_MENU_INSPECT_NATIVE_SHADER_CODE);
p_menu->add_separator();
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTRC("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_MENU_CLOSE);
p_menu->add_separator();
p_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/toggle_files_panel"), FILE_MENU_TOGGLE_FILES_PANEL);
} else {
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTRC("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_MENU_CLOSE);
p_menu->add_item(TTR("Close All"), FILE_MENU_CLOSE_ALL);
p_menu->add_item(TTR("Close Other Tabs"), FILE_MENU_CLOSE_OTHER_TABS);
if (p_type == CONTEXT_VALID_ITEM) {
p_menu->add_separator();
p_menu->add_item(TTR("Copy Script Path"), FILE_MENU_COPY_PATH);
p_menu->add_item(TTR("Show in FileSystem"), FILE_MENU_SHOW_IN_FILE_SYSTEM);
}
}
}
void ShaderEditorPlugin::_make_script_list_context_menu() {
context_menu->clear();
int selected = shader_tabs->get_current_tab();
if (selected < 0 || selected >= shader_tabs->get_tab_count()) {
return;
}
Control *control = shader_tabs->get_tab_control(selected);
bool is_valid_editor_control = Object::cast_to<TextShaderEditor>(control) || Object::cast_to<VisualShaderEditor>(control);
_setup_popup_menu(is_valid_editor_control ? CONTEXT_VALID_ITEM : CONTEXT, context_menu);
context_menu->set_item_disabled(context_menu->get_item_index(FILE_MENU_CLOSE_ALL), shader_tabs->get_tab_count() <= 0);
context_menu->set_item_disabled(context_menu->get_item_index(FILE_MENU_CLOSE_OTHER_TABS), shader_tabs->get_tab_count() <= 1);
context_menu->set_position(files_split->get_screen_position() + files_split->get_local_mouse_position());
context_menu->reset_size();
context_menu->popup();
}
void ShaderEditorPlugin::_close_shader(int p_index) {
ERR_FAIL_INDEX(p_index, shader_tabs->get_tab_count());
if (file_menu->get_parent() != nullptr) {
file_menu->get_parent()->remove_child(file_menu);
}
if (make_floating->get_parent()) {
make_floating->get_parent()->remove_child(make_floating);
}
ShaderEditor *shader_editor = Object::cast_to<ShaderEditor>(shader_tabs->get_tab_control(p_index));
ERR_FAIL_NULL(shader_editor);
memdelete(shader_editor);
edited_shaders.remove_at(p_index);
_update_shader_list();
EditorUndoRedoManager::get_singleton()->clear_history(); // To prevent undo on deleted graphs.
if (shader_tabs->get_tab_count() == 0) {
shader_list->show(); // Make sure the panel is visible, because it can't be toggled without open shaders.
} else {
_switch_to_editor(edited_shaders[shader_tabs->get_current_tab()].shader_editor);
}
}
void ShaderEditorPlugin::_close_builtin_shaders_from_scene(const String &p_scene) {
for (uint32_t i = 0; i < edited_shaders.size();) {
Ref<Shader> &shader = edited_shaders[i].shader;
if (shader.is_valid()) {
if (shader->is_built_in() && shader->get_path().begins_with(p_scene)) {
_close_shader(i);
continue;
}
}
Ref<ShaderInclude> &include = edited_shaders[i].shader_inc;
if (include.is_valid()) {
if (include->is_built_in() && include->get_path().begins_with(p_scene)) {
_close_shader(i);
continue;
}
}
i++;
}
}
void ShaderEditorPlugin::_resource_saved(Object *obj) {
// May have been renamed on save.
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader.ptr() == obj || edited_shader.shader_inc.ptr() == obj) {
_update_shader_list();
return;
}
}
}
void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
switch (p_index) {
case FILE_MENU_NEW: {
String base_path = FileSystemDock::get_singleton()->get_current_path().get_base_dir();
shader_create_dialog->config(base_path.path_join("new_shader"), false, false, 0);
shader_create_dialog->popup_centered();
} break;
case FILE_MENU_NEW_INCLUDE: {
String base_path = FileSystemDock::get_singleton()->get_current_path().get_base_dir();
shader_create_dialog->config(base_path.path_join("new_shader"), false, false, 2);
shader_create_dialog->popup_centered();
} break;
case FILE_MENU_OPEN: {
InspectorDock::get_singleton()->open_resource("Shader");
} break;
case FILE_MENU_OPEN_INCLUDE: {
InspectorDock::get_singleton()->open_resource("ShaderInclude");
} break;
case FILE_MENU_SAVE: {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor);
if (editor) {
if (editor->get_trim_trailing_whitespace_on_save()) {
editor->trim_trailing_whitespace();
}
if (editor->get_trim_final_newlines_on_save()) {
editor->trim_final_newlines();
}
}
if (edited_shaders[index].shader.is_valid()) {
EditorNode::get_singleton()->save_resource(edited_shaders[index].shader);
} else {
EditorNode::get_singleton()->save_resource(edited_shaders[index].shader_inc);
}
if (editor) {
editor->tag_saved_version();
}
} break;
case FILE_MENU_SAVE_AS: {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor);
if (editor) {
if (editor->get_trim_trailing_whitespace_on_save()) {
editor->trim_trailing_whitespace();
}
if (editor->get_trim_final_newlines_on_save()) {
editor->trim_final_newlines();
}
}
String path;
if (edited_shaders[index].shader.is_valid()) {
path = edited_shaders[index].shader->get_path();
if (!path.is_resource_file()) {
path = "";
}
EditorNode::get_singleton()->save_resource_as(edited_shaders[index].shader, path);
} else {
path = edited_shaders[index].shader_inc->get_path();
if (!path.is_resource_file()) {
path = "";
}
EditorNode::get_singleton()->save_resource_as(edited_shaders[index].shader_inc, path);
}
if (editor) {
editor->tag_saved_version();
}
} break;
case FILE_MENU_INSPECT: {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
if (edited_shaders[index].shader.is_valid()) {
EditorNode::get_singleton()->push_item(edited_shaders[index].shader.ptr());
} else {
EditorNode::get_singleton()->push_item(edited_shaders[index].shader_inc.ptr());
}
} break;
case FILE_MENU_INSPECT_NATIVE_SHADER_CODE: {
int index = shader_tabs->get_current_tab();
if (edited_shaders[index].shader.is_valid()) {
edited_shaders[index].shader->inspect_native_shader_code();
}
} break;
case FILE_MENU_CLOSE: {
_close_shader(shader_tabs->get_current_tab());
} break;
case FILE_MENU_CLOSE_ALL: {
while (shader_tabs->get_tab_count() > 0) {
_close_shader(0);
}
} break;
case FILE_MENU_CLOSE_OTHER_TABS: {
int index = shader_tabs->get_current_tab();
for (int i = 0; i < index; i++) {
_close_shader(0);
}
while (shader_tabs->get_tab_count() > 1) {
_close_shader(1);
}
} break;
case FILE_MENU_SHOW_IN_FILE_SYSTEM: {
Ref<Resource> shader = _get_current_shader();
String path = shader->get_path();
if (!path.is_empty()) {
FileSystemDock::get_singleton()->navigate_to_path(path);
}
} break;
case FILE_MENU_COPY_PATH: {
Ref<Resource> shader = _get_current_shader();
DisplayServer::get_singleton()->clipboard_set(shader->get_path());
} break;
case FILE_MENU_TOGGLE_FILES_PANEL: {
shader_list->set_visible(!shader_list->is_visible());
int index = shader_tabs->get_current_tab();
if (index != -1) {
ERR_FAIL_INDEX(index, (int)edited_shaders.size());
TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor);
if (editor) {
editor->get_code_editor()->update_toggle_files_button();
} else {
VisualShaderEditor *vs_editor = Object::cast_to<VisualShaderEditor>(edited_shaders[index].shader_editor);
if (vs_editor) {
vs_editor->update_toggle_files_button();
}
}
}
} break;
}
}
void ShaderEditorPlugin::_shader_created(Ref<Shader> p_shader) {
EditorNode::get_singleton()->push_item(p_shader.ptr());
}
void ShaderEditorPlugin::_shader_include_created(Ref<ShaderInclude> p_shader_inc) {
EditorNode::get_singleton()->push_item(p_shader_inc.ptr());
}
Variant ShaderEditorPlugin::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
if (shader_list->get_item_count() == 0) {
return Variant();
}
int idx = 0;
if (p_point == Vector2(Math::INF, Math::INF)) {
if (shader_list->is_anything_selected()) {
idx = shader_list->get_selected_items()[0];
}
} else {
idx = shader_list->get_item_at_position(p_point);
}
if (idx < 0) {
return Variant();
}
HBoxContainer *drag_preview = memnew(HBoxContainer);
String preview_name = shader_list->get_item_text(idx);
Ref<Texture2D> preview_icon = shader_list->get_item_icon(idx);
if (preview_icon.is_valid()) {
TextureRect *tf = memnew(TextureRect);
tf->set_texture(preview_icon);
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
drag_preview->add_child(tf);
}
Label *label = memnew(Label(preview_name));
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Don't translate script names.
drag_preview->add_child(label);
files_split->set_drag_preview(drag_preview);
Dictionary drag_data;
drag_data["type"] = "shader_list_element";
drag_data["shader_list_element"] = idx;
return drag_data;
}
bool ShaderEditorPlugin::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
Dictionary d = p_data;
if (!d.has("type")) {
return false;
}
if (String(d["type"]) == "shader_list_element") {
return true;
}
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
if (files.is_empty()) {
return false;
}
for (int i = 0; i < files.size(); i++) {
const String &file = files[i];
if (ResourceLoader::exists(file, "Shader")) {
Ref<Shader> shader = ResourceLoader::load(file);
if (shader.is_valid()) {
return true;
}
}
if (ResourceLoader::exists(file, "ShaderInclude")) {
Ref<ShaderInclude> sinclude = ResourceLoader::load(file);
if (sinclude.is_valid()) {
return true;
}
}
}
return false;
}
return false;
}
void ShaderEditorPlugin::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
if (!can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
Dictionary d = p_data;
if (!d.has("type")) {
return;
}
if (String(d["type"]) == "shader_list_element") {
int idx = d["shader_list_element"];
int new_idx = 0;
if (p_point == Vector2(Math::INF, Math::INF)) {
if (shader_list->is_anything_selected()) {
new_idx = shader_list->get_selected_items()[0];
}
} else {
new_idx = shader_list->get_item_at_position(p_point);
}
_move_shader_tab(idx, new_idx);
return;
}
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
for (int i = 0; i < files.size(); i++) {
const String &file = files[i];
Ref<Resource> res;
if (ResourceLoader::exists(file, "Shader") || ResourceLoader::exists(file, "ShaderInclude")) {
res = ResourceLoader::load(file);
}
if (res.is_valid()) {
edit(res.ptr());
}
}
}
}
void ShaderEditorPlugin::_window_changed(bool p_visible) {
make_floating->set_visible(!p_visible);
}
void ShaderEditorPlugin::_set_text_shader_zoom_factor(float p_zoom_factor) {
if (text_shader_zoom_factor == p_zoom_factor) {
return;
}
text_shader_zoom_factor = p_zoom_factor;
}
void ShaderEditorPlugin::_update_shader_editor_zoom_factor(CodeTextEditor *p_shader_editor) const {
if (p_shader_editor && p_shader_editor->is_visible_in_tree() && text_shader_zoom_factor != p_shader_editor->get_zoom_factor()) {
p_shader_editor->set_zoom_factor(text_shader_zoom_factor);
}
}
void ShaderEditorPlugin::_switch_to_editor(ShaderEditor *p_editor) {
if (file_menu->get_parent() != nullptr) {
file_menu->get_parent()->remove_child(file_menu);
}
if (make_floating->get_parent()) {
make_floating->get_parent()->remove_child(make_floating);
}
p_editor->use_menu_bar_items(file_menu, make_floating);
}
void ShaderEditorPlugin::_file_removed(const String &p_removed_file) {
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
if (edited_shaders[i].path == p_removed_file) {
_close_shader(i);
break;
}
}
}
void ShaderEditorPlugin::_res_saved_callback(const Ref<Resource> &p_res) {
if (p_res.is_null()) {
return;
}
const String &path = p_res->get_path();
for (EditedShader &edited : edited_shaders) {
Ref<Resource> shader_res = edited.shader;
if (shader_res.is_null()) {
shader_res = edited.shader_inc;
}
ERR_FAIL_COND(shader_res.is_null());
TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited.shader_editor);
if (!text_shader_editor || !shader_res->is_built_in()) {
continue;
}
if (shader_res->get_path().get_slice("::", 0) == path) {
text_shader_editor->tag_saved_version();
_update_shader_list();
}
}
}
void ShaderEditorPlugin::_set_file_specific_items_disabled(bool p_disabled) {
PopupMenu *file_popup_menu = file_menu->get_popup();
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_MENU_SAVE), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_MENU_SAVE_AS), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_MENU_INSPECT), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_MENU_INSPECT_NATIVE_SHADER_CODE), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_MENU_CLOSE), p_disabled);
}
void ShaderEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ShaderEditorPlugin::_resource_saved), CONNECT_DEFERRED);
EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ShaderEditorPlugin::_close_builtin_shaders_from_scene));
FileSystemDock::get_singleton()->connect("file_removed", callable_mp(this, &ShaderEditorPlugin::_file_removed));
EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ShaderEditorPlugin::_res_saved_callback));
} break;
}
}
ShaderEditorPlugin::ShaderEditorPlugin() {
window_wrapper = memnew(WindowWrapper);
window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("Shader Editor")));
window_wrapper->set_margins_enabled(true);
main_container = memnew(VBoxContainer);
Ref<Shortcut> make_floating_shortcut = ED_SHORTCUT_AND_COMMAND("shader_editor/make_floating", TTRC("Make Floating"));
window_wrapper->set_wrapped_control(main_container, make_floating_shortcut);
files_split = memnew(HSplitContainer);
files_split->set_split_offset(200 * EDSCALE);
files_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
file_menu = memnew(MenuButton);
file_menu->set_flat(false);
file_menu->set_theme_type_variation("FlatMenuButton");
file_menu->set_text(TTR("File"));
file_menu->set_switch_on_hover(true);
file_menu->set_shortcut_context(files_split);
_setup_popup_menu(FILE, file_menu->get_popup());
file_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
_set_file_specific_items_disabled(true);
context_menu = memnew(PopupMenu);
add_child(context_menu);
context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
make_floating = memnew(ScreenSelect);
make_floating->connect("request_open_in_screen", callable_mp(window_wrapper, &WindowWrapper::enable_window_on_screen).bind(true));
if (!make_floating->is_disabled()) {
// Override default ScreenSelect tooltip if multi-window support is available.
make_floating->set_tooltip_text(TTR("Make the shader editor floating.") + "\n" + TTR("Right-click to open the screen selector."));
}
window_wrapper->connect("window_visibility_changed", callable_mp(this, &ShaderEditorPlugin::_window_changed));
shader_list = memnew(ItemList);
shader_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
shader_list->set_theme_type_variation("ItemListSecondary");
shader_list->set_custom_minimum_size(Size2(100, 60) * EDSCALE);
files_split->add_child(shader_list);
shader_list->connect(SceneStringName(item_selected), callable_mp(this, &ShaderEditorPlugin::_shader_selected).bind(true));
shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked));
shader_list->set_allow_rmb_select(true);
SET_DRAG_FORWARDING_GCD(shader_list, ShaderEditorPlugin);
main_container->add_child(files_split);
main_container->set_custom_minimum_size(Size2(100, 300) * EDSCALE);
shader_tabs = memnew(TabContainer);
shader_tabs->set_custom_minimum_size(Size2(460, 300) * EDSCALE);
shader_tabs->set_tabs_visible(false);
shader_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
files_split->add_child(shader_tabs);
Ref<StyleBoxEmpty> empty;
empty.instantiate();
shader_tabs->add_theme_style_override(SceneStringName(panel), empty);
button = EditorNode::get_bottom_panel()->add_item(TTRC("Shader Editor"), window_wrapper, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_shader_editor_bottom_panel", TTRC("Toggle Shader Editor Bottom Panel"), KeyModifierMask::ALT | Key::S));
shader_create_dialog = memnew(ShaderCreateDialog);
files_split->add_child(shader_create_dialog);
shader_create_dialog->connect("shader_created", callable_mp(this, &ShaderEditorPlugin::_shader_created));
shader_create_dialog->connect("shader_include_created", callable_mp(this, &ShaderEditorPlugin::_shader_include_created));
}
ShaderEditorPlugin::~ShaderEditorPlugin() {
memdelete(file_menu);
memdelete(make_floating);
}

View File

@@ -0,0 +1,152 @@
/**************************************************************************/
/* shader_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
class CodeTextEditor;
class HSplitContainer;
class ItemList;
class MenuButton;
class ShaderCreateDialog;
class ShaderEditor;
class TabContainer;
class TextShaderEditor;
class VBoxContainer;
class HBoxContainer;
class VisualShaderEditor;
class WindowWrapper;
class ShaderEditorPlugin : public EditorPlugin {
GDCLASS(ShaderEditorPlugin, EditorPlugin);
struct EditedShader {
Ref<Shader> shader;
Ref<ShaderInclude> shader_inc;
ShaderEditor *shader_editor = nullptr;
String path;
String name;
};
LocalVector<EditedShader> edited_shaders;
enum FileMenu {
FILE_MENU_NEW,
FILE_MENU_NEW_INCLUDE,
FILE_MENU_OPEN,
FILE_MENU_OPEN_INCLUDE,
FILE_MENU_SAVE,
FILE_MENU_SAVE_AS,
FILE_MENU_INSPECT,
FILE_MENU_INSPECT_NATIVE_SHADER_CODE,
FILE_MENU_CLOSE,
FILE_MENU_CLOSE_ALL,
FILE_MENU_CLOSE_OTHER_TABS,
FILE_MENU_SHOW_IN_FILE_SYSTEM,
FILE_MENU_COPY_PATH,
FILE_MENU_TOGGLE_FILES_PANEL,
};
enum PopupMenuType {
FILE,
CONTEXT,
CONTEXT_VALID_ITEM,
};
VBoxContainer *main_container = nullptr;
HSplitContainer *files_split = nullptr;
ItemList *shader_list = nullptr;
TabContainer *shader_tabs = nullptr;
Button *button = nullptr;
MenuButton *file_menu = nullptr;
PopupMenu *context_menu = nullptr;
WindowWrapper *window_wrapper = nullptr;
Button *make_floating = nullptr;
ShaderCreateDialog *shader_create_dialog = nullptr;
float text_shader_zoom_factor = 1.0f;
Ref<Resource> _get_current_shader();
void _update_shader_list();
void _shader_selected(int p_index, bool p_push_item = true);
void _shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index);
void _setup_popup_menu(PopupMenuType p_type, PopupMenu *p_menu);
void _make_script_list_context_menu();
void _menu_item_pressed(int p_index);
void _resource_saved(Object *obj);
void _close_shader(int p_index);
void _close_builtin_shaders_from_scene(const String &p_scene);
void _file_removed(const String &p_removed_file);
void _res_saved_callback(const Ref<Resource> &p_res);
void _set_file_specific_items_disabled(bool p_disabled);
void _shader_created(Ref<Shader> p_shader);
void _shader_include_created(Ref<ShaderInclude> p_shader_inc);
void _update_shader_list_status();
void _move_shader_tab(int p_from, int p_to);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void _window_changed(bool p_visible);
void _set_text_shader_zoom_factor(float p_zoom_factor);
void _update_shader_editor_zoom_factor(CodeTextEditor *p_shader_editor) const;
void _switch_to_editor(ShaderEditor *p_editor);
protected:
void _notification(int p_what);
public:
virtual String get_plugin_name() const override { return "Shader"; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
virtual void selected_notify() override;
ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
virtual String get_unsaved_status(const String &p_for_scene) const override;
virtual void save_external_data() override;
virtual void apply_changes() override;
ShaderEditorPlugin();
~ShaderEditorPlugin();
};

View File

@@ -0,0 +1,321 @@
/**************************************************************************/
/* shader_file_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_file_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/item_list.h"
#include "scene/gui/split_container.h"
#include "servers/display_server.h"
/*** SHADER SCRIPT EDITOR ****/
/*** SCRIPT EDITOR ******/
void ShaderFileEditor::_update_version(const StringName &p_version_txt, const RD::ShaderStage p_stage) {
}
void ShaderFileEditor::_version_selected(int p_option) {
int c = versions->get_current();
StringName version_txt = versions->get_item_metadata(c);
RD::ShaderStage stage = RD::SHADER_STAGE_MAX;
int first_found = -1;
Ref<RDShaderSPIRV> bytecode = shader_file->get_spirv(version_txt);
ERR_FAIL_COND(bytecode.is_null());
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
if (bytecode->get_stage_bytecode(RD::ShaderStage(i)).is_empty() && bytecode->get_stage_compile_error(RD::ShaderStage(i)) == String()) {
stages[i]->set_button_icon(Ref<Texture2D>());
continue;
}
Ref<Texture2D> icon;
if (bytecode->get_stage_compile_error(RD::ShaderStage(i)) != String()) {
icon = get_editor_theme_icon(SNAME("ImportFail"));
} else {
icon = get_editor_theme_icon(SNAME("ImportCheck"));
}
stages[i]->set_button_icon(icon);
if (first_found == -1) {
first_found = i;
}
if (stages[i]->is_pressed()) {
stage = RD::ShaderStage(i);
break;
}
}
error_text->clear();
if (stage == RD::SHADER_STAGE_MAX) { //need to change stage, does not have it
if (first_found == -1) {
error_text->add_text(TTR("No valid shader stages found."));
return; //well you did not put any stage I guess?
}
stages[first_found]->set_pressed(true);
stage = RD::ShaderStage(first_found);
}
String error = bytecode->get_stage_compile_error(stage);
error_text->push_font(get_theme_font(SNAME("source"), EditorStringName(EditorFonts)));
if (error.is_empty()) {
error_text->add_text(TTR("Shader stage compiled without errors."));
} else {
error_text->add_text(error);
}
}
void ShaderFileEditor::_update_options() {
ERR_FAIL_COND(shader_file.is_null());
if (!shader_file->get_base_error().is_empty()) {
stage_hb->hide();
versions->hide();
error_text->clear();
error_text->push_font(get_theme_font(SNAME("source"), EditorStringName(EditorFonts)));
error_text->add_text(vformat(TTR("File structure for '%s' contains unrecoverable errors:\n\n"), shader_file->get_path().get_file()));
error_text->add_text(shader_file->get_base_error());
return;
}
stage_hb->show();
versions->show();
int c = versions->get_current();
//remember current
versions->clear();
TypedArray<StringName> version_list = shader_file->get_version_list();
if (c >= version_list.size()) {
c = version_list.size() - 1;
}
if (c < 0) {
c = 0;
}
StringName current_version;
for (int i = 0; i < version_list.size(); i++) {
String title = version_list[i];
if (title.is_empty()) {
title = "default";
}
Ref<Texture2D> icon;
Ref<RDShaderSPIRV> bytecode = shader_file->get_spirv(version_list[i]);
ERR_FAIL_COND(bytecode.is_null());
bool failed = false;
for (int j = 0; j < RD::SHADER_STAGE_MAX; j++) {
String error = bytecode->get_stage_compile_error(RD::ShaderStage(j));
if (!error.is_empty()) {
failed = true;
}
}
if (failed) {
icon = get_editor_theme_icon(SNAME("ImportFail"));
} else {
icon = get_editor_theme_icon(SNAME("ImportCheck"));
}
versions->add_item(title, icon);
versions->set_item_metadata(i, version_list[i]);
if (i == c) {
versions->select(i);
current_version = version_list[i];
}
}
if (version_list.is_empty()) {
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
stages[i]->set_disabled(true);
}
return;
}
Ref<RDShaderSPIRV> bytecode = shader_file->get_spirv(current_version);
ERR_FAIL_COND(bytecode.is_null());
int first_valid = -1;
int current = -1;
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
Vector<uint8_t> bc = bytecode->get_stage_bytecode(RD::ShaderStage(i));
String error = bytecode->get_stage_compile_error(RD::ShaderStage(i));
bool disable = error.is_empty() && bc.is_empty();
stages[i]->set_disabled(disable);
if (!disable) {
if (stages[i]->is_pressed()) {
current = i;
}
first_valid = i;
}
}
if (current == -1 && first_valid != -1) {
stages[first_valid]->set_pressed(true);
}
_version_selected(0);
}
void ShaderFileEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
if (is_visible_in_tree() && shader_file.is_valid()) {
_update_options();
}
} break;
}
}
void ShaderFileEditor::_editor_settings_changed() {
if (is_visible_in_tree() && shader_file.is_valid()) {
_update_options();
}
}
void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) {
if (p_shader.is_null()) {
if (shader_file.is_valid()) {
shader_file->disconnect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed));
}
return;
}
if (shader_file == p_shader) {
return;
}
shader_file = p_shader;
if (shader_file.is_valid()) {
shader_file->connect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed));
}
_update_options();
}
void ShaderFileEditor::_shader_changed() {
if (is_visible_in_tree()) {
_update_options();
}
}
ShaderFileEditor *ShaderFileEditor::singleton = nullptr;
ShaderFileEditor::ShaderFileEditor() {
singleton = this;
HSplitContainer *main_hs = memnew(HSplitContainer);
add_child(main_hs);
versions = memnew(ItemList);
versions->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
versions->connect(SceneStringName(item_selected), callable_mp(this, &ShaderFileEditor::_version_selected));
versions->set_custom_minimum_size(Size2i(200 * EDSCALE, 0));
versions->set_theme_type_variation("TreeSecondary");
main_hs->add_child(versions);
VBoxContainer *main_vb = memnew(VBoxContainer);
main_vb->set_h_size_flags(SIZE_EXPAND_FILL);
main_hs->add_child(main_vb);
static const char *stage_str[RD::SHADER_STAGE_MAX] = {
"Vertex",
"Fragment",
"TessControl",
"TessEval",
"Compute"
};
stage_hb = memnew(HBoxContainer);
main_vb->add_child(stage_hb);
Ref<ButtonGroup> bg;
bg.instantiate();
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
Button *button = memnew(Button(stage_str[i]));
button->set_toggle_mode(true);
button->set_focus_mode(FOCUS_ACCESSIBILITY);
stage_hb->add_child(button);
stages[i] = button;
button->set_button_group(bg);
button->connect(SceneStringName(pressed), callable_mp(this, &ShaderFileEditor::_version_selected).bind(i));
}
error_text = memnew(RichTextLabel);
error_text->set_v_size_flags(SIZE_EXPAND_FILL);
error_text->set_selection_enabled(true);
error_text->set_context_menu_enabled(true);
main_vb->add_child(error_text);
}
void ShaderFileEditorPlugin::edit(Object *p_object) {
RDShaderFile *s = Object::cast_to<RDShaderFile>(p_object);
shader_editor->edit(s);
}
bool ShaderFileEditorPlugin::handles(Object *p_object) const {
RDShaderFile *shader = Object::cast_to<RDShaderFile>(p_object);
return shader != nullptr;
}
void ShaderFileEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
EditorNode::get_bottom_panel()->make_item_visible(shader_editor);
} else {
button->hide();
if (shader_editor->is_visible_in_tree()) {
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
}
}
ShaderFileEditorPlugin::ShaderFileEditorPlugin() {
shader_editor = memnew(ShaderFileEditor);
shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
button = EditorNode::get_bottom_panel()->add_item(TTRC("ShaderFile"), shader_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_shader_file_bottom_panel", TTRC("Toggle ShaderFile Bottom Panel")));
button->hide();
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* shader_file_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
#include "servers/rendering/rendering_device_binds.h"
class ItemList;
class ShaderFileEditor : public PanelContainer {
GDCLASS(ShaderFileEditor, PanelContainer);
Ref<RDShaderFile> shader_file;
HBoxContainer *stage_hb = nullptr;
ItemList *versions = nullptr;
Button *stages[RD::SHADER_STAGE_MAX];
RichTextLabel *error_text = nullptr;
void _update_version(const StringName &p_version_txt, const RenderingDevice::ShaderStage p_stage);
void _version_selected(int p_stage);
void _editor_settings_changed();
void _update_options();
void _shader_changed();
protected:
void _notification(int p_what);
public:
static ShaderFileEditor *singleton;
void edit(const Ref<RDShaderFile> &p_shader);
ShaderFileEditor();
};
class ShaderFileEditorPlugin : public EditorPlugin {
GDCLASS(ShaderFileEditorPlugin, EditorPlugin);
ShaderFileEditor *shader_editor = nullptr;
Button *button = nullptr;
public:
virtual String get_plugin_name() const override { return "ShaderFile"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
ShaderFileEditor *get_shader_editor() const { return shader_editor; }
ShaderFileEditorPlugin();
};

View File

@@ -0,0 +1,504 @@
/**************************************************************************/
/* shader_globals_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#include "shader_globals_editor.h"
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector/editor_inspector.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "servers/rendering/shader_language.h"
static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = {
"bool",
"bvec2",
"bvec3",
"bvec4",
"int",
"ivec2",
"ivec3",
"ivec4",
"rect2i",
"uint",
"uvec2",
"uvec3",
"uvec4",
"float",
"vec2",
"vec3",
"vec4",
"color",
"rect2",
"mat2",
"mat3",
"mat4",
"transform_2d",
"transform",
"sampler2D",
"sampler2DArray",
"sampler3D",
"samplerCube",
"samplerExternalOES",
};
class ShaderGlobalsEditorInterface : public Object {
GDCLASS(ShaderGlobalsEditorInterface, Object)
void _set_var(const StringName &p_name, const Variant &p_value, const Variant &p_prev_value) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Shader Global Variable"));
undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_value);
undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_prev_value);
RS::GlobalShaderParameterType type = RS::get_singleton()->global_shader_parameter_get_type(p_name);
Dictionary gv;
gv["type"] = global_var_type_names[type];
if (type >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) {
Ref<Resource> res = p_value;
if (res.is_valid()) {
gv["value"] = res->get_path();
} else {
gv["value"] = "";
}
} else {
gv["value"] = p_value;
}
String path = "shader_globals/" + String(p_name);
undo_redo->add_do_property(ProjectSettings::get_singleton(), path, gv);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), path, GLOBAL_GET(path));
undo_redo->add_do_method(this, "_var_changed");
undo_redo->add_undo_method(this, "_var_changed");
block_update = true;
undo_redo->commit_action();
block_update = false;
}
void _var_changed() {
emit_signal(SNAME("var_changed"));
}
protected:
static void _bind_methods() {
ClassDB::bind_method("_var_changed", &ShaderGlobalsEditorInterface::_var_changed);
ADD_SIGNAL(MethodInfo("var_changed"));
}
bool _set(const StringName &p_name, const Variant &p_value) {
Variant existing = RS::get_singleton()->global_shader_parameter_get(p_name);
if (existing.get_type() == Variant::NIL) {
return false;
}
callable_mp(this, &ShaderGlobalsEditorInterface::_set_var).call_deferred(p_name, p_value, existing);
return true;
}
bool _get(const StringName &p_name, Variant &r_ret) const {
r_ret = RS::get_singleton()->global_shader_parameter_get(p_name);
return r_ret.get_type() != Variant::NIL;
}
void _get_property_list(List<PropertyInfo> *p_list) const {
Vector<StringName> variables;
variables = RS::get_singleton()->global_shader_parameter_get_list();
for (int i = 0; i < variables.size(); i++) {
PropertyInfo pinfo;
pinfo.name = variables[i];
switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) {
case RS::GLOBAL_VAR_TYPE_BOOL: {
pinfo.type = Variant::BOOL;
} break;
case RS::GLOBAL_VAR_TYPE_BVEC2: {
pinfo.type = Variant::INT;
pinfo.hint = PROPERTY_HINT_FLAGS;
pinfo.hint_string = "x,y";
} break;
case RS::GLOBAL_VAR_TYPE_BVEC3: {
pinfo.type = Variant::INT;
pinfo.hint = PROPERTY_HINT_FLAGS;
pinfo.hint_string = "x,y,z";
} break;
case RS::GLOBAL_VAR_TYPE_BVEC4: {
pinfo.type = Variant::INT;
pinfo.hint = PROPERTY_HINT_FLAGS;
pinfo.hint_string = "x,y,z,w";
} break;
case RS::GLOBAL_VAR_TYPE_INT: {
pinfo.type = Variant::INT;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC2: {
pinfo.type = Variant::VECTOR2I;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC3: {
pinfo.type = Variant::VECTOR3I;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC4: {
pinfo.type = Variant::VECTOR4I;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2I: {
pinfo.type = Variant::RECT2I;
} break;
case RS::GLOBAL_VAR_TYPE_UINT: {
pinfo.type = Variant::INT;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC2: {
pinfo.type = Variant::VECTOR2I;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC3: {
pinfo.type = Variant::VECTOR3I;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC4: {
pinfo.type = Variant::VECTOR4I;
} break;
case RS::GLOBAL_VAR_TYPE_FLOAT: {
pinfo.type = Variant::FLOAT;
} break;
case RS::GLOBAL_VAR_TYPE_VEC2: {
pinfo.type = Variant::VECTOR2;
} break;
case RS::GLOBAL_VAR_TYPE_VEC3: {
pinfo.type = Variant::VECTOR3;
} break;
case RS::GLOBAL_VAR_TYPE_VEC4: {
pinfo.type = Variant::VECTOR4;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2: {
pinfo.type = Variant::RECT2;
} break;
case RS::GLOBAL_VAR_TYPE_COLOR: {
pinfo.type = Variant::COLOR;
} break;
case RS::GLOBAL_VAR_TYPE_MAT2: {
pinfo.type = Variant::PACKED_FLOAT32_ARRAY;
} break;
case RS::GLOBAL_VAR_TYPE_MAT3: {
pinfo.type = Variant::BASIS;
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
pinfo.type = Variant::TRANSFORM2D;
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
pinfo.type = Variant::TRANSFORM3D;
} break;
case RS::GLOBAL_VAR_TYPE_MAT4: {
pinfo.type = Variant::PROJECTION;
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Texture2D";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Texture2DArray,CompressedTexture2DArray";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Texture3D";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Cubemap,CompressedCubemap";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "ExternalTexture";
} break;
default: {
} break;
}
p_list->push_back(pinfo);
}
}
public:
bool block_update = false;
ShaderGlobalsEditorInterface() {
}
};
static Variant create_var(RS::GlobalShaderParameterType p_type) {
switch (p_type) {
case RS::GLOBAL_VAR_TYPE_BOOL: {
return false;
}
case RS::GLOBAL_VAR_TYPE_BVEC2: {
return 0; //bits
}
case RS::GLOBAL_VAR_TYPE_BVEC3: {
return 0; //bits
}
case RS::GLOBAL_VAR_TYPE_BVEC4: {
return 0; //bits
}
case RS::GLOBAL_VAR_TYPE_INT: {
return 0; //bits
}
case RS::GLOBAL_VAR_TYPE_IVEC2: {
return Vector2i();
}
case RS::GLOBAL_VAR_TYPE_IVEC3: {
return Vector3i();
}
case RS::GLOBAL_VAR_TYPE_IVEC4: {
return Vector4i();
}
case RS::GLOBAL_VAR_TYPE_RECT2I: {
return Rect2i();
}
case RS::GLOBAL_VAR_TYPE_UINT: {
return 0;
}
case RS::GLOBAL_VAR_TYPE_UVEC2: {
return Vector2i();
}
case RS::GLOBAL_VAR_TYPE_UVEC3: {
return Vector3i();
}
case RS::GLOBAL_VAR_TYPE_UVEC4: {
return Vector4i();
}
case RS::GLOBAL_VAR_TYPE_FLOAT: {
return 0.0;
}
case RS::GLOBAL_VAR_TYPE_VEC2: {
return Vector2();
}
case RS::GLOBAL_VAR_TYPE_VEC3: {
return Vector3();
}
case RS::GLOBAL_VAR_TYPE_VEC4: {
return Vector4();
}
case RS::GLOBAL_VAR_TYPE_RECT2: {
return Rect2();
}
case RS::GLOBAL_VAR_TYPE_COLOR: {
return Color();
}
case RS::GLOBAL_VAR_TYPE_MAT2: {
Vector<float> xform;
xform.resize(4);
xform.write[0] = 1;
xform.write[1] = 0;
xform.write[2] = 0;
xform.write[3] = 1;
return xform;
}
case RS::GLOBAL_VAR_TYPE_MAT3: {
return Basis();
}
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
return Transform2D();
}
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
return Transform3D();
}
case RS::GLOBAL_VAR_TYPE_MAT4: {
return Projection();
}
case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
return "";
}
case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
return "";
}
case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
return "";
}
case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
return "";
}
case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: {
return "";
}
default: {
return Variant();
}
}
}
String ShaderGlobalsEditor::_check_new_variable_name(const String &p_variable_name) {
if (p_variable_name.is_empty()) {
return TTRC("Name cannot be empty.");
}
if (!p_variable_name.is_valid_ascii_identifier()) {
return TTRC("Name must be a valid identifier.");
}
return "";
}
LineEdit *ShaderGlobalsEditor::get_name_box() const {
return variable_name;
}
void ShaderGlobalsEditor::_variable_name_text_changed(const String &p_variable_name) {
const String &warning = _check_new_variable_name(p_variable_name.strip_edges());
variable_add->set_tooltip_text(warning);
variable_add->set_disabled(!warning.is_empty());
}
void ShaderGlobalsEditor::_variable_added() {
String var = variable_name->get_text().strip_edges();
if (RenderingServer::get_singleton()->global_shader_parameter_get(var).get_type() != Variant::NIL) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Global shader parameter '%s' already exists."), var));
return;
}
List<String> keywords;
ShaderLanguage::get_keyword_list(&keywords);
if (keywords.find(var) != nullptr || var == "script") {
EditorNode::get_singleton()->show_warning(vformat(TTR("Name '%s' is a reserved shader language keyword."), var));
return;
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Variant value = create_var(RS::GlobalShaderParameterType(variable_type->get_selected()));
undo_redo->create_action(TTR("Add Shader Global Parameter"));
undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_add", var, RS::GlobalShaderParameterType(variable_type->get_selected()), value);
undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_remove", var);
Dictionary gv;
gv["type"] = global_var_type_names[variable_type->get_selected()];
gv["value"] = value;
undo_redo->add_do_property(ProjectSettings::get_singleton(), "shader_globals/" + var, gv);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "shader_globals/" + var, Variant());
undo_redo->add_do_method(this, "_changed");
undo_redo->add_undo_method(this, "_changed");
undo_redo->commit_action();
variable_name->clear();
}
void ShaderGlobalsEditor::_variable_deleted(const String &p_variable) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Shader Global Parameter"));
undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_remove", p_variable);
undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_add", p_variable, RS::get_singleton()->global_shader_parameter_get_type(p_variable), RS::get_singleton()->global_shader_parameter_get(p_variable));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "shader_globals/" + p_variable, Variant());
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "shader_globals/" + p_variable, GLOBAL_GET("shader_globals/" + p_variable));
undo_redo->add_do_method(this, "_changed");
undo_redo->add_undo_method(this, "_changed");
undo_redo->commit_action();
}
void ShaderGlobalsEditor::_changed() {
emit_signal(SNAME("globals_changed"));
if (!interface->block_update) {
interface->notify_property_list_changed();
}
}
void ShaderGlobalsEditor::_bind_methods() {
ClassDB::bind_method("_changed", &ShaderGlobalsEditor::_changed);
ADD_SIGNAL(MethodInfo("globals_changed"));
}
void ShaderGlobalsEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible_in_tree()) {
inspector->edit(interface);
}
} break;
case NOTIFICATION_THEME_CHANGED: {
variable_add->set_button_icon(get_editor_theme_icon(SNAME("Add")));
} break;
case NOTIFICATION_PREDELETE: {
inspector->edit(nullptr);
} break;
}
}
ShaderGlobalsEditor::ShaderGlobalsEditor() {
ProjectSettings::get_singleton()->add_hidden_prefix("shader_globals/");
HBoxContainer *add_menu_hb = memnew(HBoxContainer);
add_child(add_menu_hb);
add_menu_hb->add_child(memnew(Label(TTRC("Name:"))));
variable_name = memnew(LineEdit);
variable_name->set_h_size_flags(SIZE_EXPAND_FILL);
variable_name->set_clear_button_enabled(true);
variable_name->connect(SceneStringName(text_changed), callable_mp(this, &ShaderGlobalsEditor::_variable_name_text_changed));
variable_name->connect(SceneStringName(text_submitted), callable_mp(this, &ShaderGlobalsEditor::_variable_added).unbind(1));
add_menu_hb->add_child(variable_name);
add_menu_hb->add_child(memnew(Label(TTRC("Type:"))));
variable_type = memnew(OptionButton);
variable_type->set_h_size_flags(SIZE_EXPAND_FILL);
add_menu_hb->add_child(variable_type);
for (int i = 0; i < RS::GLOBAL_VAR_TYPE_MAX; i++) {
variable_type->add_item(global_var_type_names[i]);
}
variable_add = memnew(Button(TTRC("Add")));
variable_add->set_disabled(true);
add_menu_hb->add_child(variable_add);
variable_add->connect(SceneStringName(pressed), callable_mp(this, &ShaderGlobalsEditor::_variable_added));
inspector = memnew(EditorInspector);
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(inspector);
inspector->set_use_wide_editors(true);
inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);
inspector->set_use_deletable_properties(true);
inspector->connect("property_deleted", callable_mp(this, &ShaderGlobalsEditor::_variable_deleted), CONNECT_DEFERRED);
interface = memnew(ShaderGlobalsEditorInterface);
interface->connect("var_changed", callable_mp(this, &ShaderGlobalsEditor::_changed));
}
ShaderGlobalsEditor::~ShaderGlobalsEditor() {
memdelete(interface);
}

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* shader_globals_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_sectioned_inspector.h"
#include "scene/gui/box_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/tree.h"
class ShaderGlobalsEditorInterface;
class ShaderGlobalsEditor : public VBoxContainer {
GDCLASS(ShaderGlobalsEditor, VBoxContainer)
ShaderGlobalsEditorInterface *interface = nullptr;
EditorInspector *inspector = nullptr;
LineEdit *variable_name = nullptr;
OptionButton *variable_type = nullptr;
Button *variable_add = nullptr;
String _check_new_variable_name(const String &p_variable_name);
void _variable_name_text_changed(const String &p_variable_name);
void _variable_added();
void _variable_deleted(const String &p_variable);
void _changed();
protected:
static void _bind_methods();
void _notification(int p_what);
public:
LineEdit *get_name_box() const;
ShaderGlobalsEditor();
~ShaderGlobalsEditor();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,214 @@
/**************************************************************************/
/* text_shader_editor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/gui/code_editor.h"
#include "editor/shader/shader_editor.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/rich_text_label.h"
#include "servers/rendering/shader_warnings.h"
class GDShaderSyntaxHighlighter : public CodeHighlighter {
GDCLASS(GDShaderSyntaxHighlighter, CodeHighlighter)
private:
Vector<Point2i> disabled_branch_regions;
Color disabled_branch_color;
public:
virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
void add_disabled_branch_region(const Point2i &p_region);
void clear_disabled_branch_regions();
void set_disabled_branch_color(const Color &p_color);
};
class ShaderTextEditor : public CodeTextEditor {
GDCLASS(ShaderTextEditor, CodeTextEditor);
Color marked_line_color = Color(1, 1, 1);
struct WarningsComparator {
_ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); }
};
Ref<GDShaderSyntaxHighlighter> syntax_highlighter;
RichTextLabel *warnings_panel = nullptr;
Ref<Shader> shader;
Ref<ShaderInclude> shader_inc;
List<ShaderWarning> warnings;
Error last_compile_result = Error::OK;
void _check_shader_mode();
void _update_warning_panel();
bool block_shader_changed = false;
void _shader_changed();
uint32_t dependencies_version = 0; // Incremented if deps changed
protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _load_theme_settings() override;
virtual void _code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) override;
public:
void set_block_shader_changed(bool p_block) { block_shader_changed = p_block; }
uint32_t get_dependencies_version() const { return dependencies_version; }
virtual void _validate_script() override;
void reload_text();
void set_warnings_panel(RichTextLabel *p_warnings_panel);
Ref<Shader> get_edited_shader() const;
Ref<ShaderInclude> get_edited_shader_include() const;
void set_edited_shader(const Ref<Shader> &p_shader);
void set_edited_shader(const Ref<Shader> &p_shader, const String &p_code);
void set_edited_shader_include(const Ref<ShaderInclude> &p_include);
void set_edited_shader_include(const Ref<ShaderInclude> &p_include, const String &p_code);
void set_edited_code(const String &p_code);
ShaderTextEditor();
};
class TextShaderEditor : public ShaderEditor {
GDCLASS(TextShaderEditor, ShaderEditor);
enum {
EDIT_UNDO,
EDIT_REDO,
EDIT_CUT,
EDIT_COPY,
EDIT_PASTE,
EDIT_SELECT_ALL,
EDIT_MOVE_LINE_UP,
EDIT_MOVE_LINE_DOWN,
EDIT_INDENT,
EDIT_UNINDENT,
EDIT_DELETE_LINE,
EDIT_DUPLICATE_SELECTION,
EDIT_DUPLICATE_LINES,
EDIT_TOGGLE_WORD_WRAP,
EDIT_TOGGLE_COMMENT,
EDIT_COMPLETE,
SEARCH_FIND,
SEARCH_FIND_NEXT,
SEARCH_FIND_PREV,
SEARCH_REPLACE,
SEARCH_GOTO_LINE,
BOOKMARK_TOGGLE,
BOOKMARK_GOTO_NEXT,
BOOKMARK_GOTO_PREV,
BOOKMARK_REMOVE_ALL,
HELP_DOCS,
EDIT_EMOJI_AND_SYMBOL,
};
HBoxContainer *menu_bar_hbox = nullptr;
MenuButton *edit_menu = nullptr;
MenuButton *search_menu = nullptr;
PopupMenu *bookmarks_menu = nullptr;
Button *site_search = nullptr;
PopupMenu *context_menu = nullptr;
RichTextLabel *warnings_panel = nullptr;
uint64_t idle = 0;
GotoLinePopup *goto_line_popup = nullptr;
ConfirmationDialog *erase_tab_confirm = nullptr;
ConfirmationDialog *disk_changed = nullptr;
ShaderTextEditor *code_editor = nullptr;
bool compilation_success = true;
void _menu_option(int p_option);
void _prepare_edit_menu();
mutable Ref<Shader> shader;
mutable Ref<ShaderInclude> shader_inc;
void _editor_settings_changed();
void _apply_editor_settings();
void _project_settings_changed();
void _check_for_external_edit();
void _reload_shader_from_disk();
void _reload_shader_include_from_disk();
void _reload();
void _show_warnings_panel(bool p_show);
void _warning_clicked(const Variant &p_line);
void _update_warnings(bool p_validate);
void _script_validated(bool p_valid) {
compilation_success = p_valid;
emit_signal(SNAME("validation_changed"));
}
uint32_t dependencies_version = 0xFFFFFFFF;
bool trim_trailing_whitespace_on_save;
bool trim_final_newlines_on_save;
protected:
void _notification(int p_what);
static void _bind_methods();
void _make_context_menu(bool p_selection, Vector2 p_position);
void _text_edit_gui_input(const Ref<InputEvent> &p_ev);
void _update_bookmark_list();
void _bookmark_item_pressed(int p_idx);
public:
virtual void edit_shader(const Ref<Shader> &p_shader) override;
virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) override;
virtual void use_menu_bar_items(MenuButton *p_file_menu, Button *p_make_floating) override;
virtual void apply_shaders() override;
virtual bool is_unsaved() const override;
virtual void save_external_data(const String &p_str = "") override;
virtual void validate_script() override;
bool was_compilation_successful() const { return compilation_success; }
bool get_trim_trailing_whitespace_on_save() const { return trim_trailing_whitespace_on_save; }
bool get_trim_final_newlines_on_save() const { return trim_final_newlines_on_save; }
void ensure_select_current();
void goto_line_selection(int p_line, int p_begin, int p_end);
void trim_trailing_whitespace();
void trim_final_newlines();
void tag_saved_version();
ShaderTextEditor *get_code_editor() { return code_editor; }
virtual Size2 get_minimum_size() const override { return Size2(0, 200); }
TextShaderEditor();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,739 @@
/**************************************************************************/
/* visual_shader_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_properties.h"
#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/shader/shader_editor.h"
#include "scene/gui/graph_edit.h"
#include "scene/resources/syntax_highlighter.h"
#include "scene/resources/visual_shader.h"
class CodeEdit;
class ColorPicker;
class CurveEditor;
class GraphElement;
class GraphFrame;
class HFlowContainer;
class MenuButton;
class PopupPanel;
class RichTextLabel;
class Tree;
class VisualShaderEditor;
class MaterialEditor;
class VisualShaderNodePlugin : public RefCounted {
GDCLASS(VisualShaderNodePlugin, RefCounted);
protected:
VisualShaderEditor *vseditor = nullptr;
protected:
static void _bind_methods();
GDVIRTUAL2RC(Object *, _create_editor, Ref<Resource>, Ref<VisualShaderNode>)
public:
void set_editor(VisualShaderEditor *p_editor);
virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node);
};
class VSGraphNode : public GraphNode {
GDCLASS(VSGraphNode, GraphNode);
protected:
void _draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color, const Color &p_rim_color);
virtual void draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) override;
};
class VSRerouteNode : public VSGraphNode {
GDCLASS(VSRerouteNode, GraphNode);
const float FADE_ANIMATION_LENGTH_SEC = 0.3;
float icon_opacity = 0.0;
protected:
void _notification(int p_what);
virtual void draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) override;
public:
VSRerouteNode();
void set_icon_opacity(float p_opacity);
void _on_mouse_entered();
void _on_mouse_exited();
};
class VisualShaderGraphPlugin : public RefCounted {
GDCLASS(VisualShaderGraphPlugin, RefCounted);
private:
VisualShaderEditor *editor = nullptr;
struct InputPort {
Button *default_input_button = nullptr;
};
struct Port {
VisualShaderNode::PortType type = VisualShaderNode::PORT_TYPE_SCALAR;
TextureButton *preview_button = nullptr;
};
struct Link {
VisualShader::Type type = VisualShader::Type::TYPE_MAX;
VisualShaderNode *visual_node = nullptr;
GraphElement *graph_element = nullptr;
bool preview_visible = false;
int preview_pos = 0;
HashMap<int, InputPort> input_ports;
HashMap<int, Port> output_ports;
VBoxContainer *preview_box = nullptr;
LineEdit *parameter_name = nullptr;
CodeEdit *expression_edit = nullptr;
CurveEditor *curve_editors[3] = { nullptr, nullptr, nullptr };
};
Ref<VisualShader> visual_shader;
HashMap<int, Link> links;
List<VisualShader::Connection> connections;
Color vector_expanded_color[4];
// Visual shader specific theme for using MSDF fonts (on GraphNodes) which reduce aliasing at higher zoom levels.
Ref<Theme> vs_msdf_fonts_theme;
protected:
static void _bind_methods();
public:
void set_editor(VisualShaderEditor *p_editor);
void register_shader(VisualShader *p_visual_shader);
void set_connections(const List<VisualShader::Connection> &p_connections);
void register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphElement *p_graph_element);
void register_output_port(int p_id, int p_port, VisualShaderNode::PortType p_port_type, TextureButton *p_button);
void register_parameter_name(int p_id, LineEdit *p_parameter_name);
void register_default_input_button(int p_node_id, int p_port_id, Button *p_button);
void register_expression_edit(int p_node_id, CodeEdit *p_expression_edit);
void register_curve_editor(int p_node_id, int p_index, CurveEditor *p_curve_editor);
void clear_links();
void set_shader_type(VisualShader::Type p_type);
bool is_preview_visible(int p_id) const;
void update_node(VisualShader::Type p_type, int p_id);
void update_node_deferred(VisualShader::Type p_type, int p_node_id);
void add_node(VisualShader::Type p_type, int p_id, bool p_just_update, bool p_update_frames);
void remove_node(VisualShader::Type p_type, int p_id, bool p_just_update);
void connect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void disconnect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id, bool p_is_valid);
void update_frames(VisualShader::Type p_type, int p_node);
void set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position);
void refresh_node_ports(VisualShader::Type p_type, int p_node);
void set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, const Variant &p_value);
void update_parameter_refs();
void set_parameter_name(VisualShader::Type p_type, int p_node_id, const String &p_name);
void update_curve(int p_node_id);
void update_curve_xyz(int p_node_id);
void set_expression(VisualShader::Type p_type, int p_node_id, const String &p_expression);
void attach_node_to_frame(VisualShader::Type p_type, int p_node_id, int p_frame_id);
void detach_node_from_frame(VisualShader::Type p_type, int p_node_id);
void set_frame_color_enabled(VisualShader::Type p_type, int p_node_id, bool p_enable);
void set_frame_color(VisualShader::Type p_type, int p_node_id, const Color &p_color);
void set_frame_autoshrink_enabled(VisualShader::Type p_type, int p_node_id, bool p_enable);
void update_reroute_nodes();
int get_constant_index(float p_constant) const;
Ref<Script> get_node_script(int p_node_id) const;
void update_theme();
bool is_node_has_parameter_instances_relatively(VisualShader::Type p_type, int p_node) const;
VisualShaderGraphPlugin();
};
class VisualShaderEditedProperty : public RefCounted {
GDCLASS(VisualShaderEditedProperty, RefCounted);
private:
Variant edited_property;
protected:
static void _bind_methods();
public:
void set_edited_property(const Variant &p_variant);
Variant get_edited_property() const;
};
class VisualShaderEditor : public ShaderEditor {
GDCLASS(VisualShaderEditor, ShaderEditor);
friend class VisualShaderGraphPlugin;
Ref<ConfigFile> vs_editor_cache; // Keeps the graph offsets and zoom levels for each VisualShader that has been edited.
PopupPanel *property_editor_popup = nullptr;
EditorProperty *property_editor = nullptr;
int editing_node = -1;
int editing_port = -1;
Ref<VisualShaderEditedProperty> edited_property_holder;
MaterialEditor *material_editor = nullptr;
Ref<VisualShader> visual_shader;
Ref<ShaderMaterial> preview_material;
Ref<Environment> env;
String param_filter_name;
EditorProperty *current_prop = nullptr;
VBoxContainer *shader_preview_vbox = nullptr;
Button *site_search = nullptr;
Button *toggle_files_button = nullptr;
Control *toggle_files_list = nullptr;
GraphEdit *graph = nullptr;
Button *add_node = nullptr;
MenuButton *varying_button = nullptr;
Button *code_preview_button = nullptr;
Button *shader_preview_button = nullptr;
HFlowContainer *toolbar_hflow = nullptr;
int last_to_node = -1;
int last_to_port = -1;
Label *info_label = nullptr;
OptionButton *edit_type = nullptr;
OptionButton *edit_type_standard = nullptr;
OptionButton *edit_type_particles = nullptr;
OptionButton *edit_type_sky = nullptr;
OptionButton *edit_type_fog = nullptr;
CheckBox *custom_mode_box = nullptr;
bool custom_mode_enabled = false;
bool pending_update_preview = false;
bool shader_error = false;
AcceptDialog *code_preview_window = nullptr;
VBoxContainer *code_preview_vbox = nullptr;
CodeEdit *preview_text = nullptr;
Ref<CodeHighlighter> syntax_highlighter = nullptr;
PanelContainer *error_panel = nullptr;
Label *error_label = nullptr;
bool pending_custom_scripts_to_delete = false;
List<Ref<Script>> custom_scripts_to_delete;
bool _block_update_options_menu = false;
bool _block_rebuild_shader = false;
Point2 saved_node_pos;
bool saved_node_pos_dirty = false;
ConfirmationDialog *members_dialog = nullptr;
VisualShaderNode::PortType members_input_port_type = VisualShaderNode::PORT_TYPE_MAX;
VisualShaderNode::PortType members_output_port_type = VisualShaderNode::PORT_TYPE_MAX;
PopupMenu *popup_menu = nullptr;
PopupMenu *connection_popup_menu = nullptr;
PopupMenu *constants_submenu = nullptr;
MenuButton *tools = nullptr;
ConfirmationDialog *add_varying_dialog = nullptr;
OptionButton *varying_type = nullptr;
LineEdit *varying_name = nullptr;
OptionButton *varying_mode = nullptr;
Label *varying_error_label = nullptr;
ConfirmationDialog *remove_varying_dialog = nullptr;
Tree *varyings = nullptr;
PopupPanel *frame_title_change_popup = nullptr;
LineEdit *frame_title_change_edit = nullptr;
PopupPanel *frame_tint_color_pick_popup = nullptr;
ColorPicker *frame_tint_color_picker = nullptr;
bool code_preview_first = true;
bool code_preview_showed = false;
bool shader_preview_showed = true;
LineEdit *param_filter = nullptr;
MenuButton *preview_tools = nullptr;
String selected_param_id;
Tree *parameters = nullptr;
HashMap<String, PropertyInfo> parameter_props;
VBoxContainer *param_vbox = nullptr;
VBoxContainer *param_vbox2 = nullptr;
enum ShaderModeFlags {
MODE_FLAGS_SPATIAL_CANVASITEM = 1,
MODE_FLAGS_SKY = 2,
MODE_FLAGS_PARTICLES = 4,
MODE_FLAGS_FOG = 8,
};
int mode = MODE_FLAGS_SPATIAL_CANVASITEM;
VisualShader::Type current_type = VisualShader::Type::TYPE_VERTEX; // The type of the currently edited VisualShader.
enum TypeFlags {
TYPE_FLAGS_VERTEX = 1,
TYPE_FLAGS_FRAGMENT = 2,
TYPE_FLAGS_LIGHT = 4,
};
enum ParticlesTypeFlags {
TYPE_FLAGS_EMIT = 1,
TYPE_FLAGS_PROCESS = 2,
TYPE_FLAGS_COLLIDE = 4,
TYPE_FLAGS_EMIT_CUSTOM = 8,
TYPE_FLAGS_PROCESS_CUSTOM = 16,
};
enum SkyTypeFlags {
TYPE_FLAGS_SKY = 1,
};
enum FogTypeFlags {
TYPE_FLAGS_FOG = 1,
};
enum ToolsMenuOptions {
EXPAND_ALL,
COLLAPSE_ALL
};
enum PreviewToolsMenuOptions {
COPY_PARAMS_FROM_MATERIAL,
PASTE_PARAMS_TO_MATERIAL,
};
enum NodeMenuOptions {
ADD,
SEPARATOR, // ignore
CUT,
COPY,
PASTE,
DELETE_, // Conflict with WinAPI.
DUPLICATE,
CLEAR_COPY_BUFFER,
SEPARATOR2, // ignore
FLOAT_CONSTANTS,
CONVERT_CONSTANTS_TO_PARAMETERS,
CONVERT_PARAMETERS_TO_CONSTANTS,
UNLINK_FROM_PARENT_FRAME,
SEPARATOR3, // ignore
SET_FRAME_TITLE,
ENABLE_FRAME_COLOR,
SET_FRAME_COLOR,
ENABLE_FRAME_AUTOSHRINK,
};
enum ConnectionMenuOptions {
INSERT_NEW_NODE,
INSERT_NEW_REROUTE,
DISCONNECT,
};
enum class VaryingMenuOptions {
ADD,
REMOVE,
};
Tree *members = nullptr;
AcceptDialog *alert = nullptr;
LineEdit *node_filter = nullptr;
RichTextLabel *node_desc = nullptr;
Label *highend_label = nullptr;
void _tools_menu_option(int p_idx);
void _show_members_dialog(bool at_mouse_pos, VisualShaderNode::PortType p_input_port_type = VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PortType p_output_port_type = VisualShaderNode::PORT_TYPE_MAX);
void _varying_menu_id_pressed(int p_idx);
void _show_add_varying_dialog();
void _show_remove_varying_dialog();
void _preview_tools_menu_option(int p_idx);
void _clear_preview_param();
void _update_preview_parameter_list();
bool _update_preview_parameter_tree();
void _update_nodes();
void _update_graph();
void _restore_editor_state();
String _get_cache_id_string() const;
String _get_cache_key(const String &p_prop_name) const;
struct AddOption {
String name;
String category;
String type;
String description;
Vector<Variant> ops;
Ref<Script> script;
int mode = 0;
int return_type = 0;
int func = 0;
bool highend = false;
bool is_custom = false;
bool is_native = false;
mutable int temp_idx = 0;
AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String(), const String &p_description = String(), const Vector<Variant> &p_ops = Vector<Variant>(), int p_return_type = -1, int p_mode = -1, int p_func = -1, bool p_highend = false) {
name = p_name;
type = p_type;
category = p_category;
description = p_description;
ops = p_ops;
return_type = p_return_type;
mode = p_mode;
func = p_func;
highend = p_highend;
}
};
struct _OptionComparator {
_FORCE_INLINE_ bool operator()(const AddOption &a, const AddOption &b) const {
return a.category.count("/") > b.category.count("/") || (a.category + "/" + a.name).naturalnocasecmp_to(b.category + "/" + b.name) < 0;
}
};
Vector<AddOption> add_options;
int cubemap_node_option_idx;
int texture2d_node_option_idx;
int texture2d_array_node_option_idx;
int texture3d_node_option_idx;
int custom_node_option_idx;
int curve_node_option_idx;
int curve_xyz_node_option_idx;
int mesh_emitter_option_idx;
List<String> keyword_list;
List<VisualShaderNodeParameterRef> uniform_refs;
void _draw_color_over_button(Object *p_obj, Color p_color);
void _setup_node(VisualShaderNode *p_node, const Vector<Variant> &p_ops);
void _add_node(int p_idx, const Vector<Variant> &p_ops, const String &p_resource_path = "", int p_node_idx = -1);
void _add_varying(const String &p_name, VisualShader::VaryingMode p_mode, VisualShader::VaryingType p_type);
void _remove_varying(const String &p_name);
void _update_options_menu();
void _set_mode(int p_which);
void _show_preview_text();
void _preview_close_requested();
void _preview_size_changed();
void _update_preview();
void _update_next_previews(int p_node_id);
void _get_next_nodes_recursively(VisualShader::Type p_type, int p_node_id, LocalVector<int> &r_nodes) const;
String _get_description(int p_idx);
void _show_shader_preview();
void _toggle_files_pressed();
Vector<int> nodes_link_to_frame_buffer; // Contains the nodes that are requested to be linked to a frame. This is used to perform one Undo/Redo operation for dragging nodes.
int frame_node_id_to_link_to = -1;
struct DragOp {
VisualShader::Type type = VisualShader::Type::TYPE_MAX;
int node = 0;
Vector2 from;
Vector2 to;
};
List<DragOp> drag_buffer;
Timer *panning_debounce_timer = nullptr;
bool shader_fully_loaded = false;
bool drag_dirty = false;
void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node);
void _nodes_dragged();
void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
void _scroll_offset_changed(const Vector2 &p_scroll);
void _node_selected(Object *p_node);
void _delete_nodes(int p_type, const List<int> &p_nodes);
void _delete_node_request(int p_type, int p_node);
void _delete_nodes_request(const TypedArray<StringName> &p_nodes);
void _node_changed(int p_id);
void _nodes_linked_to_frame_request(const TypedArray<StringName> &p_nodes, const StringName &p_frame);
void _frame_rect_changed(const GraphFrame *p_frame, const Rect2 &p_new_rect);
void _edit_port_default_input(Object *p_button, int p_node, int p_port);
void _port_edited(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
int to_node = -1;
int to_slot = -1;
int from_node = -1;
int from_slot = -1;
Ref<GraphEdit::Connection> clicked_connection;
bool connection_node_insert_requested = false;
HashSet<int> selected_constants;
HashSet<int> selected_parameters;
int selected_frame = -1;
int selected_float_constant = -1;
void _convert_constants_to_parameters(bool p_vice_versa);
void _detach_nodes_from_frame_request();
void _detach_nodes_from_frame(int p_type, const List<int> &p_nodes);
void _replace_node(VisualShader::Type p_type_id, int p_node_id, const StringName &p_from, const StringName &p_to);
void _update_constant(VisualShader::Type p_type_id, int p_node_id, const Variant &p_var, int p_preview_port);
void _update_parameter(VisualShader::Type p_type_id, int p_node_id, const Variant &p_var, int p_preview_port);
void _unlink_node_from_parent_frame(int p_node_id);
void _connection_drag_ended();
void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
bool _check_node_drop_on_connection(const Vector2 &p_position, Ref<GraphEdit::Connection> *r_closest_connection, int *r_node_id = nullptr, int *r_to_port = nullptr);
void _handle_node_drop_on_connection();
void _frame_title_popup_show(const Point2 &p_position, int p_node_id);
void _frame_title_popup_hide();
void _frame_title_popup_focus_out();
void _frame_title_text_changed(const String &p_new_text);
void _frame_title_text_submitted(const String &p_new_text);
void _frame_color_enabled_changed(int p_node_id);
void _frame_color_popup_show(const Point2 &p_position, int p_node_id);
void _frame_color_popup_hide();
void _frame_color_confirm();
void _frame_color_changed(const Color &p_color);
void _frame_autoshrink_enabled_changed(int p_node_id);
void _parameter_line_edit_changed(const String &p_text, int p_node_id);
void _parameter_line_edit_focus_out(Object *p_line_edit, int p_node_id);
void _port_name_focus_out(Object *p_line_edit, int p_node_id, int p_port_id, bool p_output);
struct CopyItem {
int id;
Ref<VisualShaderNode> node;
Vector2 position;
Vector2 size;
String group_inputs;
String group_outputs;
String expression;
bool disabled = false;
};
void _dup_copy_nodes(int p_type, List<CopyItem> &r_nodes, List<VisualShader::Connection> &r_connections);
void _dup_paste_nodes(int p_type, List<CopyItem> &r_items, const List<VisualShader::Connection> &p_connections, const Vector2 &p_offset, bool p_duplicate);
void _duplicate_nodes();
static Vector2 selection_center;
static List<CopyItem> copy_items_buffer;
static List<VisualShader::Connection> copy_connections_buffer;
void _clear_copy_buffer();
void _copy_nodes(bool p_cut);
void _paste_nodes(bool p_use_custom_position = false, const Vector2 &p_custom_position = Vector2());
Vector<Ref<VisualShaderNodePlugin>> plugins;
Ref<VisualShaderGraphPlugin> graph_plugin;
void _type_selected(int p_id);
void _custom_mode_toggled(bool p_enabled);
void _input_select_item(Ref<VisualShaderNodeInput> p_input, const String &p_name);
void _parameter_ref_select_item(Ref<VisualShaderNodeParameterRef> p_parameter_ref, const String &p_name);
void _varying_select_item(Ref<VisualShaderNodeVarying> p_varying, const String &p_name);
void _float_constant_selected(int p_which);
void _add_input_port(int p_node, int p_port, int p_port_type, const String &p_name);
void _remove_input_port(int p_node, int p_port);
void _change_input_port_type(int p_type, int p_node, int p_port);
void _change_input_port_name(const String &p_text, Object *p_line_edit, int p_node, int p_port);
void _add_output_port(int p_node, int p_port, int p_port_type, const String &p_name);
void _remove_output_port(int p_node, int p_port);
void _change_output_port_type(int p_type, int p_node, int p_port);
void _change_output_port_name(const String &p_text, Object *p_line_edit, int p_node, int p_port);
void _expand_output_port(int p_node, int p_port, bool p_expand);
void _expression_focus_out(Object *p_code_edit, int p_node);
void _set_node_size(int p_type, int p_node, const Size2 &p_size);
void _node_resized(const Vector2 &p_new_size, int p_type, int p_node);
void _preview_select_port(int p_node, int p_port);
void _graph_gui_input(const Ref<InputEvent> &p_event);
void _member_filter_changed(const String &p_text);
void _sbox_input(const Ref<InputEvent> &p_event);
void _member_selected();
void _member_create();
void _member_cancel();
void _varying_create();
void _varying_validate();
void _varying_type_changed(int p_index);
void _varying_mode_changed(int p_index);
void _varying_name_changed(const String &p_name);
void _varying_deleted();
void _varying_selected();
void _varying_unselected();
void _update_varying_tree();
void _set_custom_node_option(int p_index, int p_node, int p_op);
Vector2 menu_point;
void _node_menu_id_pressed(int p_idx);
void _connection_menu_id_pressed(int p_idx);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool _is_available(int p_mode);
void _update_parameters(bool p_update_refs);
void _update_parameter_refs(HashSet<String> &p_names);
void _update_varyings();
void _update_options_menu_deferred();
void _rebuild_shader_deferred();
void _visibility_changed();
void _get_current_mode_limits(int &r_begin_type, int &r_end_type) const;
void _update_custom_script(const Ref<Script> &p_script);
void _script_created(const Ref<Script> &p_script);
void _resource_saved(const Ref<Resource> &p_resource);
void _resource_removed(const Ref<Resource> &p_resource);
void _resources_removed();
void _param_property_changed(const String &p_property, const Variant &p_value, const String &p_field = "", bool p_changing = false);
void _update_current_param();
void _param_filter_changed(const String &p_text);
void _param_selected();
void _param_unselected();
void _help_open();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
virtual void edit_shader(const Ref<Shader> &p_shader) override;
virtual void use_menu_bar_items(MenuButton *p_file_menu, Button *p_make_floating) override;
virtual void apply_shaders() override;
virtual bool is_unsaved() const override;
virtual void save_external_data(const String &p_str = "") override;
virtual void validate_script() override;
void save_editor_layout();
void set_current_shader_type(VisualShader::Type p_type);
VisualShader::Type get_current_shader_type() const;
void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
VisualShaderGraphPlugin *get_graph_plugin() { return graph_plugin.ptr(); }
void clear_custom_types();
void add_custom_type(const String &p_name, const String &p_type, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend);
void set_toggle_list_control(Control *p_control);
Dictionary get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node);
void update_custom_type(const Ref<Resource> &p_resource);
virtual Size2 get_minimum_size() const override;
void update_toggle_files_button();
Ref<VisualShader> get_visual_shader() const { return visual_shader; }
VisualShaderEditor();
~VisualShaderEditor();
};
class VisualShaderNodePluginDefault : public VisualShaderNodePlugin {
GDCLASS(VisualShaderNodePluginDefault, VisualShaderNodePlugin);
public:
virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node) override;
};
class EditorPropertyVisualShaderMode : public EditorProperty {
GDCLASS(EditorPropertyVisualShaderMode, EditorProperty);
OptionButton *options = nullptr;
void _option_selected(int p_which);
public:
void setup(const Vector<String> &p_options);
virtual void update_property() override;
void set_option_button_clip(bool p_enable);
EditorPropertyVisualShaderMode();
};
class EditorInspectorVisualShaderModePlugin : public EditorInspectorPlugin {
GDCLASS(EditorInspectorVisualShaderModePlugin, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
class VisualShaderNodePortPreview : public Control {
GDCLASS(VisualShaderNodePortPreview, Control);
TextureRect *checkerboard = nullptr;
Ref<VisualShader> shader;
Ref<ShaderMaterial> preview_mat;
VisualShader::Type type = VisualShader::Type::TYPE_MAX;
int node = 0;
int port = 0;
bool is_valid = false;
void _shader_changed(); //must regen
protected:
void _notification(int p_what);
public:
virtual Size2 get_minimum_size() const override;
void setup(const Ref<VisualShader> &p_shader, Ref<ShaderMaterial> &p_preview_material, VisualShader::Type p_type, bool p_has_transparency, int p_node, int p_port, bool p_is_valid);
};
class VisualShaderConversionPlugin : public EditorResourceConversionPlugin {
GDCLASS(VisualShaderConversionPlugin, EditorResourceConversionPlugin);
public:
virtual String converts_to() const override;
virtual bool handles(const Ref<Resource> &p_resource) const override;
virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override;
};