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
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:
8
editor/shader/SCsub
Normal file
8
editor/shader/SCsub
Normal 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")
|
160
editor/shader/editor_native_shader_source_visualizer.cpp
Normal file
160
editor/shader/editor_native_shader_source_visualizer.cpp
Normal 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"));
|
||||
}
|
50
editor/shader/editor_native_shader_source_visualizer.h
Normal file
50
editor/shader/editor_native_shader_source_visualizer.h
Normal 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();
|
||||
};
|
13
editor/shader/shader_baker/SCsub
Normal file
13
editor/shader/shader_baker/SCsub
Normal 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")
|
@@ -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));
|
||||
}
|
||||
}
|
@@ -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;
|
||||
};
|
@@ -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";
|
||||
}
|
@@ -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;
|
||||
};
|
@@ -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";
|
||||
}
|
@@ -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;
|
||||
};
|
687
editor/shader/shader_create_dialog.cpp
Normal file
687
editor/shader/shader_create_dialog.cpp
Normal 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"));
|
||||
}
|
109
editor/shader/shader_create_dialog.h
Normal file
109
editor/shader/shader_create_dialog.h
Normal 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();
|
||||
};
|
51
editor/shader/shader_editor.h
Normal file
51
editor/shader/shader_editor.h
Normal 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;
|
||||
};
|
919
editor/shader/shader_editor_plugin.cpp
Normal file
919
editor/shader/shader_editor_plugin.cpp
Normal 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);
|
||||
}
|
152
editor/shader/shader_editor_plugin.h
Normal file
152
editor/shader/shader_editor_plugin.h
Normal 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();
|
||||
};
|
321
editor/shader/shader_file_editor_plugin.cpp
Normal file
321
editor/shader/shader_file_editor_plugin.cpp
Normal 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();
|
||||
}
|
84
editor/shader/shader_file_editor_plugin.h
Normal file
84
editor/shader/shader_file_editor_plugin.h
Normal 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();
|
||||
};
|
504
editor/shader/shader_globals_editor.cpp
Normal file
504
editor/shader/shader_globals_editor.cpp
Normal 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);
|
||||
}
|
66
editor/shader/shader_globals_editor.h
Normal file
66
editor/shader/shader_globals_editor.h
Normal 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();
|
||||
};
|
1335
editor/shader/text_shader_editor.cpp
Normal file
1335
editor/shader/text_shader_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
214
editor/shader/text_shader_editor.h
Normal file
214
editor/shader/text_shader_editor.h
Normal 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();
|
||||
};
|
8457
editor/shader/visual_shader_editor_plugin.cpp
Normal file
8457
editor/shader/visual_shader_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
739
editor/shader/visual_shader_editor_plugin.h
Normal file
739
editor/shader/visual_shader_editor_plugin.h
Normal 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;
|
||||
};
|
Reference in New Issue
Block a user