Path: blob/master/editor/scene/texture/texture_layered_editor_plugin.cpp
20912 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 "core/input/input.h"33#include "editor/editor_string_names.h"34#include "editor/scene/texture/color_channel_selector.h"35#include "editor/themes/editor_scale.h"36#include "scene/gui/label.h"3738// Shader sources.3940constexpr const char *array_2d_shader = R"(41// TextureLayeredEditor preview shader (2D array).4243shader_type canvas_item;4445uniform sampler2DArray tex;46uniform float layer;47uniform vec4 u_channel_factors = vec4(1.0);4849vec4 filter_preview_colors(vec4 input_color, vec4 factors) {50// Filter RGB.51vec4 output_color = input_color * vec4(factors.rgb, input_color.a);5253// Remove transparency when alpha is not enabled.54output_color.a = mix(1.0, output_color.a, factors.a);5556// Switch to opaque grayscale when visualizing only one channel.57float csum = factors.r + factors.g + factors.b + factors.a;58float single = clamp(2.0 - csum, 0.0, 1.0);59for (int i = 0; i < 4; i++) {60float c = input_color[i];61output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);62}6364return output_color;65}6667void fragment() {68COLOR = textureLod(tex, vec3(UV, layer), 0.0);69COLOR = filter_preview_colors(COLOR, u_channel_factors);70}71)";7273constexpr const char *cubemap_shader = R"(74// TextureLayeredEditor preview shader (cubemap).7576shader_type canvas_item;7778uniform samplerCube tex;79uniform vec3 normal;80uniform mat3 rot;8182uniform vec4 u_channel_factors = vec4(1.0);8384vec4 filter_preview_colors(vec4 input_color, vec4 factors) {85// Filter RGB.86vec4 output_color = input_color * vec4(factors.rgb, input_color.a);8788// Remove transparency when alpha is not enabled.89output_color.a = mix(1.0, output_color.a, factors.a);9091// Switch to opaque grayscale when visualizing only one channel.92float csum = factors.r + factors.g + factors.b + factors.a;93float single = clamp(2.0 - csum, 0.0, 1.0);94for (int i = 0; i < 4; i++) {95float c = input_color[i];96output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);97}9899return output_color;100}101102void fragment() {103vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));104COLOR = textureLod(tex, n, 0.0);105COLOR = filter_preview_colors(COLOR, u_channel_factors);106}107)";108109constexpr const char *cubemap_array_shader = R"(110// TextureLayeredEditor preview shader (cubemap array).111112shader_type canvas_item;113uniform samplerCubeArray tex;114uniform vec3 normal;115uniform mat3 rot;116uniform float layer;117118uniform vec4 u_channel_factors = vec4(1.0);119120vec4 filter_preview_colors(vec4 input_color, vec4 factors) {121// Filter RGB.122vec4 output_color = input_color * vec4(factors.rgb, input_color.a);123124// Remove transparency when alpha is not enabled.125output_color.a = mix(1.0, output_color.a, factors.a);126127// Switch to opaque grayscale when visualizing only one channel.128float csum = factors.r + factors.g + factors.b + factors.a;129float single = clamp(2.0 - csum, 0.0, 1.0);130for (int i = 0; i < 4; i++) {131float c = input_color[i];132output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);133}134135return output_color;136}137138void fragment() {139vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));140COLOR = textureLod(tex, vec4(n, layer), 0.0);141COLOR = filter_preview_colors(COLOR, u_channel_factors);142}143)";144145void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) {146ERR_FAIL_COND(p_event.is_null());147148if (!use_rotation) {149return;150}151152Ref<InputEventMouseMotion> mm = p_event;153if (mm.is_valid() && mm->get_button_mask().has_flag(MouseButtonMask::RIGHT)) {154if (Input::get_singleton()->get_mouse_mode() == Input::MouseMode::MOUSE_MODE_VISIBLE) {155Input::get_singleton()->set_mouse_mode(Input::MouseMode::MOUSE_MODE_CAPTURED);156}157158y_rot += mm->get_relative().x * 0.01;159x_rot = CLAMP(x_rot - mm->get_relative().y * 0.01, -Math::PI * 0.5f, Math::PI * 0.5f);160161_update_material(false);162}163164Ref<InputEventMouseButton> mb = p_event;165if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT) {166if (Input::get_singleton()->get_mouse_mode() == Input::MouseMode::MOUSE_MODE_CAPTURED) {167Input::get_singleton()->set_mouse_mode(Input::MouseMode::MOUSE_MODE_VISIBLE);168Input::get_singleton()->warp_mouse(original_mouse_pos);169} else if (Input::get_singleton()->get_mouse_mode() == Input::MouseMode::MOUSE_MODE_VISIBLE) {170original_mouse_pos = mb->get_global_position();171}172}173}174175void TextureLayeredEditor::_texture_rect_draw() {176texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1));177}178179void TextureLayeredEditor::_update_gui() {180if (texture.is_null()) {181return;182}183184_texture_rect_update_area();185186const Image::Format format = texture->get_format();187const String format_name = Image::get_format_name(format);188String texture_info;189190switch (texture->get_layered_type()) {191case TextureLayered::LAYERED_TYPE_2D_ARRAY: {192layer->set_max(texture->get_layers() - 1);193194texture_info = vformat(String::utf8("%d×%d (×%d) %s\n"),195texture->get_width(),196texture->get_height(),197texture->get_layers(),198format_name);199200} break;201case TextureLayered::LAYERED_TYPE_CUBEMAP: {202layer->hide();203204texture_info = vformat(String::utf8("%d×%d %s\n"),205texture->get_width(),206texture->get_height(),207format_name);208209} break;210case TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY: {211layer->set_max(texture->get_layers() / 6 - 1);212213texture_info = vformat(String::utf8("%d×%d (×%d) %s\n"),214texture->get_width(),215texture->get_height(),216texture->get_layers() / 6,217format_name);218219} break;220221default: {222}223}224225if (texture->has_mipmaps()) {226const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);227const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_layers();228229texture_info += vformat(TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),230mip_count,231String::humanize_size(memory));232233} else {234const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_layers();235236texture_info += vformat(TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),237String::humanize_size(memory));238}239240info->set_text(texture_info);241242const uint32_t components_mask = Image::get_format_component_mask(format);243if (is_power_of_2(components_mask)) {244// Only one channel available, no point in showing a channel selector.245channel_selector->hide();246} else {247channel_selector->show();248channel_selector->set_available_channels_mask(components_mask);249}250}251252void TextureLayeredEditor::_notification(int p_what) {253switch (p_what) {254case NOTIFICATION_RESIZED: {255_texture_rect_update_area();256} break;257258case NOTIFICATION_DRAW: {259Ref<Texture2D> checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));260draw_texture_rect(checkerboard, texture_rect->get_rect(), true);261_draw_outline();262} break;263264case NOTIFICATION_THEME_CHANGED: {265if (info) {266Ref<Font> metadata_label_font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));267info->add_theme_font_override(SceneStringName(font), metadata_label_font);268}269theme_cache.outline_color = get_theme_color(SNAME("extra_border_color_1"), EditorStringName(Editor));270} break;271}272}273274void TextureLayeredEditor::_texture_changed() {275if (!is_visible()) {276return;277}278279setting = true;280_update_gui();281setting = false;282283_update_material(true);284queue_redraw();285}286287void TextureLayeredEditor::_update_material(bool p_texture_changed) {288materials[0]->set_shader_parameter("layer", layer->get_value());289materials[2]->set_shader_parameter("layer", layer->get_value());290291Vector3 v(-1, -1, -1);292v.normalize();293294Basis b;295b.rotate(Vector3(1, 0, 0), x_rot);296b.rotate(Vector3(0, 1, 0), y_rot);297298materials[1]->set_shader_parameter("normal", v);299materials[1]->set_shader_parameter("rot", b);300materials[2]->set_shader_parameter("normal", v);301materials[2]->set_shader_parameter("rot", b);302303if (p_texture_changed) {304const TextureLayered::LayeredType type = texture->get_layered_type();305use_rotation = type == TextureLayered::LAYERED_TYPE_CUBEMAP || type == TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY;306307materials[texture->get_layered_type()]->set_shader_parameter("tex", texture->get_rid());308}309310const Vector4 channel_factors = channel_selector->get_selected_channel_factors();311for (unsigned int i = 0; i < 3; ++i) {312materials[i]->set_shader_parameter("u_channel_factors", channel_factors);313}314}315316void TextureLayeredEditor::on_selected_channels_changed() {317_update_material(false);318}319320void TextureLayeredEditor::_draw_outline() {321const float outline_width = Math::round(EDSCALE);322const Rect2 outline_rect = texture_rect->get_rect().grow(outline_width * 0.5);323draw_rect(outline_rect, theme_cache.outline_color, false, outline_width);324}325326void TextureLayeredEditor::_make_materials() {327for (int i = 0; i < 3; i++) {328materials[i].instantiate();329materials[i]->set_shader(shaders[i]);330}331}332333void TextureLayeredEditor::_texture_rect_update_area() {334Size2 size = get_size();335int tex_width = texture->get_width() * size.height / texture->get_height();336int tex_height = size.height;337338if (tex_width > size.width) {339tex_width = size.width;340tex_height = texture->get_height() * tex_width / texture->get_width();341}342343// Prevent the texture from being unpreviewable after the rescale, so that we can still see something344if (tex_height <= 0) {345tex_height = 1;346}347if (tex_width <= 0) {348tex_width = 1;349}350351int ofs_x = (size.width - tex_width) / 2;352int ofs_y = (size.height - tex_height) / 2;353354texture_rect->set_position(Vector2(ofs_x, ofs_y - Math::round(EDSCALE)));355texture_rect->set_size(Vector2(tex_width, tex_height));356}357358void TextureLayeredEditor::init_shaders() {359shaders[0].instantiate();360shaders[0]->set_code(array_2d_shader);361362shaders[1].instantiate();363shaders[1]->set_code(cubemap_shader);364365shaders[2].instantiate();366shaders[2]->set_code(cubemap_array_shader);367}368369void TextureLayeredEditor::finish_shaders() {370shaders[0].unref();371shaders[1].unref();372shaders[2].unref();373}374375void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {376if (texture.is_valid()) {377texture->disconnect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));378}379380texture = p_texture;381382if (texture.is_valid()) {383if (materials[0].is_null()) {384_make_materials();385}386387texture->connect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));388texture_rect->set_material(materials[texture->get_layered_type()]);389390setting = true;391layer->set_value(0);392layer->show();393_update_gui();394setting = false;395396x_rot = 0;397y_rot = 0;398399_update_material(true);400queue_redraw();401402} else {403hide();404}405}406407TextureLayeredEditor::TextureLayeredEditor() {408set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);409set_custom_minimum_size(Size2(0, 256.0) * EDSCALE);410411texture_rect = memnew(Control);412texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE);413texture_rect->connect(SceneStringName(draw), callable_mp(this, &TextureLayeredEditor::_texture_rect_draw));414415add_child(texture_rect);416417layer = memnew(SpinBox);418layer->set_step(1);419layer->set_max(100);420421layer->set_modulate(Color(1, 1, 1, 0.8));422layer->set_h_grow_direction(GROW_DIRECTION_BEGIN);423layer->set_anchor(SIDE_RIGHT, 1);424layer->set_anchor(SIDE_LEFT, 1);425layer->connect(SceneStringName(value_changed), callable_mp(this, &TextureLayeredEditor::_layer_changed));426427add_child(layer);428429channel_selector = memnew(ColorChannelSelector);430channel_selector->connect("selected_channels_changed", callable_mp(this, &TextureLayeredEditor::on_selected_channels_changed));431channel_selector->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);432add_child(channel_selector);433434info = memnew(Label);435info->set_focus_mode(FOCUS_ACCESSIBILITY);436info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));437info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));438info->add_theme_font_size_override(SceneStringName(font_size), 14 * EDSCALE);439info->add_theme_color_override("font_outline_color", Color(0, 0, 0));440info->add_theme_constant_override("outline_size", 8 * EDSCALE);441442info->set_h_grow_direction(GROW_DIRECTION_BEGIN);443info->set_v_grow_direction(GROW_DIRECTION_BEGIN);444info->set_h_size_flags(Control::SIZE_SHRINK_END);445info->set_v_size_flags(Control::SIZE_SHRINK_END);446info->set_anchor(SIDE_RIGHT, 1);447info->set_anchor(SIDE_LEFT, 1);448info->set_anchor(SIDE_BOTTOM, 1);449info->set_anchor(SIDE_TOP, 1);450451add_child(info);452}453454bool EditorInspectorPluginLayeredTexture::can_handle(Object *p_object) {455return Object::cast_to<TextureLayered>(p_object) != nullptr;456}457458void EditorInspectorPluginLayeredTexture::parse_begin(Object *p_object) {459TextureLayered *texture = Object::cast_to<TextureLayered>(p_object);460if (!texture) {461return;462}463Ref<TextureLayered> m(texture);464465TextureLayeredEditor *editor = memnew(TextureLayeredEditor);466editor->edit(m);467add_custom_control(editor);468}469470TextureLayeredEditorPlugin::TextureLayeredEditorPlugin() {471Ref<EditorInspectorPluginLayeredTexture> plugin;472plugin.instantiate();473add_inspector_plugin(plugin);474}475476477