Path: blob/master/editor/scene/texture/texture_layered_editor_plugin.cpp
9912 views
/**************************************************************************/1/* texture_layered_editor_plugin.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "texture_layered_editor_plugin.h"3132#include "editor/editor_string_names.h"33#include "editor/scene/texture/color_channel_selector.h"34#include "editor/themes/editor_scale.h"35#include "scene/gui/label.h"3637// Shader sources.3839constexpr const char *array_2d_shader = R"(40// TextureLayeredEditor preview shader (2D array).4142shader_type canvas_item;4344uniform sampler2DArray tex;45uniform float layer;46uniform vec4 u_channel_factors = vec4(1.0);4748vec4 filter_preview_colors(vec4 input_color, vec4 factors) {49// Filter RGB.50vec4 output_color = input_color * vec4(factors.rgb, input_color.a);5152// Remove transparency when alpha is not enabled.53output_color.a = mix(1.0, output_color.a, factors.a);5455// Switch to opaque grayscale when visualizing only one channel.56float csum = factors.r + factors.g + factors.b + factors.a;57float single = clamp(2.0 - csum, 0.0, 1.0);58for (int i = 0; i < 4; i++) {59float c = input_color[i];60output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);61}6263return output_color;64}6566void fragment() {67COLOR = textureLod(tex, vec3(UV, layer), 0.0);68COLOR = filter_preview_colors(COLOR, u_channel_factors);69}70)";7172constexpr const char *cubemap_shader = R"(73// TextureLayeredEditor preview shader (cubemap).7475shader_type canvas_item;7677uniform samplerCube tex;78uniform vec3 normal;79uniform mat3 rot;8081uniform vec4 u_channel_factors = vec4(1.0);8283vec4 filter_preview_colors(vec4 input_color, vec4 factors) {84// Filter RGB.85vec4 output_color = input_color * vec4(factors.rgb, input_color.a);8687// Remove transparency when alpha is not enabled.88output_color.a = mix(1.0, output_color.a, factors.a);8990// Switch to opaque grayscale when visualizing only one channel.91float csum = factors.r + factors.g + factors.b + factors.a;92float single = clamp(2.0 - csum, 0.0, 1.0);93for (int i = 0; i < 4; i++) {94float c = input_color[i];95output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);96}9798return output_color;99}100101void fragment() {102vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));103COLOR = textureLod(tex, n, 0.0);104COLOR = filter_preview_colors(COLOR, u_channel_factors);105}106)";107108constexpr const char *cubemap_array_shader = R"(109// TextureLayeredEditor preview shader (cubemap array).110111shader_type canvas_item;112uniform samplerCubeArray tex;113uniform vec3 normal;114uniform mat3 rot;115uniform float layer;116117uniform vec4 u_channel_factors = vec4(1.0);118119vec4 filter_preview_colors(vec4 input_color, vec4 factors) {120// Filter RGB.121vec4 output_color = input_color * vec4(factors.rgb, input_color.a);122123// Remove transparency when alpha is not enabled.124output_color.a = mix(1.0, output_color.a, factors.a);125126// Switch to opaque grayscale when visualizing only one channel.127float csum = factors.r + factors.g + factors.b + factors.a;128float single = clamp(2.0 - csum, 0.0, 1.0);129for (int i = 0; i < 4; i++) {130float c = input_color[i];131output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);132}133134return output_color;135}136137void fragment() {138vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));139COLOR = textureLod(tex, vec4(n, layer), 0.0);140COLOR = filter_preview_colors(COLOR, u_channel_factors);141}142)";143144void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) {145ERR_FAIL_COND(p_event.is_null());146147Ref<InputEventMouseMotion> mm = p_event;148if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {149y_rot += -mm->get_relative().x * 0.01;150x_rot += -mm->get_relative().y * 0.01;151152_update_material(false);153}154}155156void TextureLayeredEditor::_texture_rect_draw() {157texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1));158}159160void TextureLayeredEditor::_update_gui() {161if (texture.is_null()) {162return;163}164165_texture_rect_update_area();166167const Image::Format format = texture->get_format();168const String format_name = Image::get_format_name(format);169String texture_info;170171switch (texture->get_layered_type()) {172case TextureLayered::LAYERED_TYPE_2D_ARRAY: {173layer->set_max(texture->get_layers() - 1);174175texture_info = vformat(String::utf8("%d×%d (×%d) %s\n"),176texture->get_width(),177texture->get_height(),178texture->get_layers(),179format_name);180181} break;182case TextureLayered::LAYERED_TYPE_CUBEMAP: {183layer->hide();184185texture_info = vformat(String::utf8("%d×%d %s\n"),186texture->get_width(),187texture->get_height(),188format_name);189190} break;191case TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY: {192layer->set_max(texture->get_layers() / 6 - 1);193194texture_info = vformat(String::utf8("%d×%d (×%d) %s\n"),195texture->get_width(),196texture->get_height(),197texture->get_layers() / 6,198format_name);199200} break;201202default: {203}204}205206if (texture->has_mipmaps()) {207const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);208const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_layers();209210texture_info += vformat(TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),211mip_count,212String::humanize_size(memory));213214} else {215const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_layers();216217texture_info += vformat(TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),218String::humanize_size(memory));219}220221info->set_text(texture_info);222223const uint32_t components_mask = Image::get_format_component_mask(format);224if (is_power_of_2(components_mask)) {225// Only one channel available, no point in showing a channel selector.226channel_selector->hide();227} else {228channel_selector->show();229channel_selector->set_available_channels_mask(components_mask);230}231}232233void TextureLayeredEditor::_notification(int p_what) {234switch (p_what) {235case NOTIFICATION_RESIZED: {236_texture_rect_update_area();237} break;238239case NOTIFICATION_DRAW: {240Ref<Texture2D> checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));241draw_texture_rect(checkerboard, texture_rect->get_rect(), true);242_draw_outline();243} break;244245case NOTIFICATION_THEME_CHANGED: {246if (info) {247Ref<Font> metadata_label_font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));248info->add_theme_font_override(SceneStringName(font), metadata_label_font);249}250theme_cache.outline_color = get_theme_color(SNAME("extra_border_color_1"), EditorStringName(Editor));251} break;252}253}254255void TextureLayeredEditor::_texture_changed() {256if (!is_visible()) {257return;258}259260setting = true;261_update_gui();262setting = false;263264_update_material(true);265queue_redraw();266}267268void TextureLayeredEditor::_update_material(bool p_texture_changed) {269materials[0]->set_shader_parameter("layer", layer->get_value());270materials[2]->set_shader_parameter("layer", layer->get_value());271272Vector3 v(1, 1, 1);273v.normalize();274275Basis b;276b.rotate(Vector3(1, 0, 0), x_rot);277b.rotate(Vector3(0, 1, 0), y_rot);278279materials[1]->set_shader_parameter("normal", v);280materials[1]->set_shader_parameter("rot", b);281materials[2]->set_shader_parameter("normal", v);282materials[2]->set_shader_parameter("rot", b);283284if (p_texture_changed) {285materials[texture->get_layered_type()]->set_shader_parameter("tex", texture->get_rid());286}287288const Vector4 channel_factors = channel_selector->get_selected_channel_factors();289for (unsigned int i = 0; i < 3; ++i) {290materials[i]->set_shader_parameter("u_channel_factors", channel_factors);291}292}293294void TextureLayeredEditor::on_selected_channels_changed() {295_update_material(false);296}297298void TextureLayeredEditor::_draw_outline() {299const float outline_width = Math::round(EDSCALE);300const Rect2 outline_rect = texture_rect->get_rect().grow(outline_width * 0.5);301draw_rect(outline_rect, theme_cache.outline_color, false, outline_width);302}303304void TextureLayeredEditor::_make_shaders() {305shaders[0].instantiate();306shaders[0]->set_code(array_2d_shader);307308shaders[1].instantiate();309shaders[1]->set_code(cubemap_shader);310311shaders[2].instantiate();312shaders[2]->set_code(cubemap_array_shader);313314for (int i = 0; i < 3; i++) {315materials[i].instantiate();316materials[i]->set_shader(shaders[i]);317}318}319320void TextureLayeredEditor::_texture_rect_update_area() {321Size2 size = get_size();322int tex_width = texture->get_width() * size.height / texture->get_height();323int tex_height = size.height;324325if (tex_width > size.width) {326tex_width = size.width;327tex_height = texture->get_height() * tex_width / texture->get_width();328}329330// Prevent the texture from being unpreviewable after the rescale, so that we can still see something331if (tex_height <= 0) {332tex_height = 1;333}334if (tex_width <= 0) {335tex_width = 1;336}337338int ofs_x = (size.width - tex_width) / 2;339int ofs_y = (size.height - tex_height) / 2;340341texture_rect->set_position(Vector2(ofs_x, ofs_y - Math::round(EDSCALE)));342texture_rect->set_size(Vector2(tex_width, tex_height));343}344345void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {346if (texture.is_valid()) {347texture->disconnect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));348}349350texture = p_texture;351352if (texture.is_valid()) {353if (shaders[0].is_null()) {354_make_shaders();355}356357texture->connect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));358texture_rect->set_material(materials[texture->get_layered_type()]);359360setting = true;361layer->set_value(0);362layer->show();363_update_gui();364setting = false;365366x_rot = 0;367y_rot = 0;368369_update_material(true);370queue_redraw();371372} else {373hide();374}375}376377TextureLayeredEditor::TextureLayeredEditor() {378set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);379set_custom_minimum_size(Size2(0, 256.0) * EDSCALE);380381texture_rect = memnew(Control);382texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE);383texture_rect->connect(SceneStringName(draw), callable_mp(this, &TextureLayeredEditor::_texture_rect_draw));384385add_child(texture_rect);386387layer = memnew(SpinBox);388layer->set_step(1);389layer->set_max(100);390391layer->set_modulate(Color(1, 1, 1, 0.8));392layer->set_h_grow_direction(GROW_DIRECTION_BEGIN);393layer->set_anchor(SIDE_RIGHT, 1);394layer->set_anchor(SIDE_LEFT, 1);395layer->connect(SceneStringName(value_changed), callable_mp(this, &TextureLayeredEditor::_layer_changed));396397add_child(layer);398399channel_selector = memnew(ColorChannelSelector);400channel_selector->connect("selected_channels_changed", callable_mp(this, &TextureLayeredEditor::on_selected_channels_changed));401channel_selector->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);402add_child(channel_selector);403404info = memnew(Label);405info->set_focus_mode(FOCUS_ACCESSIBILITY);406info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));407info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));408info->add_theme_font_size_override(SceneStringName(font_size), 14 * EDSCALE);409info->add_theme_color_override("font_outline_color", Color(0, 0, 0));410info->add_theme_constant_override("outline_size", 8 * EDSCALE);411412info->set_h_grow_direction(GROW_DIRECTION_BEGIN);413info->set_v_grow_direction(GROW_DIRECTION_BEGIN);414info->set_h_size_flags(Control::SIZE_SHRINK_END);415info->set_v_size_flags(Control::SIZE_SHRINK_END);416info->set_anchor(SIDE_RIGHT, 1);417info->set_anchor(SIDE_LEFT, 1);418info->set_anchor(SIDE_BOTTOM, 1);419info->set_anchor(SIDE_TOP, 1);420421add_child(info);422}423424bool EditorInspectorPluginLayeredTexture::can_handle(Object *p_object) {425return Object::cast_to<TextureLayered>(p_object) != nullptr;426}427428void EditorInspectorPluginLayeredTexture::parse_begin(Object *p_object) {429TextureLayered *texture = Object::cast_to<TextureLayered>(p_object);430if (!texture) {431return;432}433Ref<TextureLayered> m(texture);434435TextureLayeredEditor *editor = memnew(TextureLayeredEditor);436editor->edit(m);437add_custom_control(editor);438}439440TextureLayeredEditorPlugin::TextureLayeredEditorPlugin() {441Ref<EditorInspectorPluginLayeredTexture> plugin;442plugin.instantiate();443add_inspector_plugin(plugin);444}445446447