mirror of
https://github.com/godotengine/godot.git
synced 2025-01-22 18:43:29 -05:00
Merge pull request #100157 from Zylann/texture_preview_channel_selector
Add color channel filter to editor texture previews
This commit is contained in:
commit
f07e3ed551
13 changed files with 582 additions and 38 deletions
|
@ -1135,7 +1135,7 @@ static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::is_size_po2() const {
|
bool Image::is_size_po2() const {
|
||||||
return uint32_t(width) == next_power_of_2(width) && uint32_t(height) == next_power_of_2(height);
|
return is_power_of_2(width) && is_power_of_2(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) {
|
void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) {
|
||||||
|
@ -3953,6 +3953,97 @@ String Image::get_format_name(Format p_format) {
|
||||||
return format_names[p_format];
|
return format_names[p_format];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Image::get_format_component_mask(Format p_format) {
|
||||||
|
const uint32_t r = 1;
|
||||||
|
const uint32_t rg = 3;
|
||||||
|
const uint32_t rgb = 7;
|
||||||
|
const uint32_t rgba = 15;
|
||||||
|
|
||||||
|
switch (p_format) {
|
||||||
|
case FORMAT_L8:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_LA8:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_R8:
|
||||||
|
return r;
|
||||||
|
case FORMAT_RG8:
|
||||||
|
return rg;
|
||||||
|
case FORMAT_RGB8:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_RGBA8:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_RGBA4444:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_RGB565:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_RF:
|
||||||
|
return r;
|
||||||
|
case FORMAT_RGF:
|
||||||
|
return rg;
|
||||||
|
case FORMAT_RGBF:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_RGBAF:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_RH:
|
||||||
|
return r;
|
||||||
|
case FORMAT_RGH:
|
||||||
|
return rg;
|
||||||
|
case FORMAT_RGBH:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_RGBAH:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_RGBE9995:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_DXT1:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_DXT3:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_DXT5:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_RGTC_R:
|
||||||
|
return r;
|
||||||
|
case FORMAT_RGTC_RG:
|
||||||
|
return rg;
|
||||||
|
case FORMAT_BPTC_RGBA:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_BPTC_RGBF:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_BPTC_RGBFU:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_ETC:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_ETC2_R11:
|
||||||
|
return r;
|
||||||
|
case FORMAT_ETC2_R11S:
|
||||||
|
return r;
|
||||||
|
case FORMAT_ETC2_RG11:
|
||||||
|
return rg;
|
||||||
|
case FORMAT_ETC2_RG11S:
|
||||||
|
return rg;
|
||||||
|
case FORMAT_ETC2_RGB8:
|
||||||
|
return rgb;
|
||||||
|
case FORMAT_ETC2_RGBA8:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_ETC2_RGB8A1:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_ETC2_RA_AS_RG:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_DXT5_RA_AS_RG:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_ASTC_4x4:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_ASTC_4x4_HDR:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_ASTC_8x8:
|
||||||
|
return rgba;
|
||||||
|
case FORMAT_ASTC_8x8_HDR:
|
||||||
|
return rgba;
|
||||||
|
default:
|
||||||
|
ERR_PRINT("Unhandled format.");
|
||||||
|
return rgba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error Image::load_png_from_buffer(const Vector<uint8_t> &p_array) {
|
Error Image::load_png_from_buffer(const Vector<uint8_t> &p_array) {
|
||||||
return _load_from_buffer(p_array, _png_mem_loader_func);
|
return _load_from_buffer(p_array, _png_mem_loader_func);
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,6 +395,7 @@ public:
|
||||||
Ref<Image> get_region(const Rect2i &p_area) const;
|
Ref<Image> get_region(const Rect2i &p_area) const;
|
||||||
|
|
||||||
static String get_format_name(Format p_format);
|
static String get_format_name(Format p_format);
|
||||||
|
static uint32_t get_format_component_mask(Format p_format);
|
||||||
|
|
||||||
Error load_png_from_buffer(const Vector<uint8_t> &p_array);
|
Error load_png_from_buffer(const Vector<uint8_t> &p_array);
|
||||||
Error load_jpg_from_buffer(const Vector<uint8_t> &p_array);
|
Error load_jpg_from_buffer(const Vector<uint8_t> &p_array);
|
||||||
|
|
|
@ -64,10 +64,6 @@ SafeNumeric<uint64_t> Memory::max_usage;
|
||||||
|
|
||||||
SafeNumeric<uint64_t> Memory::alloc_count;
|
SafeNumeric<uint64_t> Memory::alloc_count;
|
||||||
|
|
||||||
inline bool is_power_of_2(size_t x) {
|
|
||||||
return x && ((x & (x - 1U)) == 0U);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *Memory::alloc_aligned_static(size_t p_bytes, size_t p_alignment) {
|
void *Memory::alloc_aligned_static(size_t p_bytes, size_t p_alignment) {
|
||||||
DEV_ASSERT(is_power_of_2(p_alignment));
|
DEV_ASSERT(is_power_of_2(p_alignment));
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,12 @@ constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) {
|
||||||
|
|
||||||
/* Functions to handle powers of 2 and shifting. */
|
/* Functions to handle powers of 2 and shifting. */
|
||||||
|
|
||||||
|
// Returns `true` if a positive integer is a power of 2, `false` otherwise.
|
||||||
|
template <typename T>
|
||||||
|
inline bool is_power_of_2(const T x) {
|
||||||
|
return x && ((x & (x - 1)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Function to find the next power of 2 to an integer.
|
// Function to find the next power of 2 to an integer.
|
||||||
static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) {
|
static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
|
|
1
editor/icons/TexturePreviewChannels.svg
Normal file
1
editor/icons/TexturePreviewChannels.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="none" stroke="#000" stroke-linejoin="round" stroke-opacity=".8" stroke-width="2" d="m8 1 7 3v8l-7 3-7-3V4Z"/><path fill-opacity=".8" d="M2 5h12v6l-12-.008Z" paint-order="stroke fill markers"/><path fill="#c2c2c2" d="m8 7.5-7 3V12l7 3 7-3v-1.5z"/><path fill="#d6d6d6" d="m8 15-7-3v-1.5l7 3z"/><path fill="#f9f9f9" d="m1 10.5 7 3 7-3-7-3z"/><path fill="#c2c2c2" d="m8 4.25-7 3v1.5l7 3 7-3v-1.5z"/><path fill="#d6d6d6" d="m8 11.75-7-3v-1.5l7 3z"/><path fill="#f9f9f9" d="m1 7.25 7 3 7-3-7-3z"/><path fill="#c2c2c2" d="M8 1 1 4v1.5l7 3 7-3V4Z"/><path fill="#f9f9f9" d="m1 4 7 3 7-3-7-3z"/><path fill="#d6d6d6" d="m8 8.5-7-3V4l7 3z"/></svg>
|
After Width: | Height: | Size: 710 B |
146
editor/plugins/color_channel_selector.cpp
Normal file
146
editor/plugins/color_channel_selector.cpp
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/**************************************************************************/
|
||||||
|
/* color_channel_selector.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 "color_channel_selector.h"
|
||||||
|
|
||||||
|
#include "editor/themes/editor_scale.h"
|
||||||
|
#include "scene/gui/box_container.h"
|
||||||
|
#include "scene/gui/button.h"
|
||||||
|
#include "scene/gui/panel_container.h"
|
||||||
|
#include "scene/resources/style_box_flat.h"
|
||||||
|
|
||||||
|
ColorChannelSelector::ColorChannelSelector() {
|
||||||
|
toggle_button = memnew(Button);
|
||||||
|
toggle_button->set_flat(true);
|
||||||
|
toggle_button->set_toggle_mode(true);
|
||||||
|
toggle_button->connect(SceneStringName(toggled), callable_mp(this, &ColorChannelSelector::on_toggled));
|
||||||
|
toggle_button->add_theme_style_override("focus", memnew(StyleBoxEmpty));
|
||||||
|
add_child(toggle_button);
|
||||||
|
|
||||||
|
panel = memnew(PanelContainer);
|
||||||
|
panel->hide();
|
||||||
|
|
||||||
|
HBoxContainer *container = memnew(HBoxContainer);
|
||||||
|
container->add_theme_constant_override("separation", 0);
|
||||||
|
|
||||||
|
create_button(0, "R", container);
|
||||||
|
create_button(1, "G", container);
|
||||||
|
create_button(2, "B", container);
|
||||||
|
create_button(3, "A", container);
|
||||||
|
|
||||||
|
// Use a bit of transparency to be less distracting.
|
||||||
|
set_modulate(Color(1, 1, 1, 0.7));
|
||||||
|
|
||||||
|
panel->add_child(container);
|
||||||
|
|
||||||
|
add_child(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorChannelSelector::_notification(int p_what) {
|
||||||
|
if (p_what == NOTIFICATION_THEME_CHANGED) {
|
||||||
|
// PanelContainer's background is invisible in the editor. We need a background.
|
||||||
|
// And we need this in turn because buttons don't look good without background (for example, hover is transparent).
|
||||||
|
Ref<StyleBox> bg_style = get_theme_stylebox(SceneStringName(panel), "TabContainer");
|
||||||
|
ERR_FAIL_COND(bg_style.is_null());
|
||||||
|
bg_style = bg_style->duplicate();
|
||||||
|
// The default content margin makes the widget become a bit too large. It should be like mini-toolbar.
|
||||||
|
const float editor_scale = EditorScale::get_scale();
|
||||||
|
bg_style->set_content_margin(SIDE_LEFT, 1.0f * editor_scale);
|
||||||
|
bg_style->set_content_margin(SIDE_RIGHT, 1.0f * editor_scale);
|
||||||
|
bg_style->set_content_margin(SIDE_TOP, 1.0f * editor_scale);
|
||||||
|
bg_style->set_content_margin(SIDE_BOTTOM, 1.0f * editor_scale);
|
||||||
|
panel->add_theme_style_override(SceneStringName(panel), bg_style);
|
||||||
|
|
||||||
|
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("TexturePreviewChannels"));
|
||||||
|
toggle_button->set_button_icon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorChannelSelector::set_available_channels_mask(uint32_t p_mask) {
|
||||||
|
for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) {
|
||||||
|
const bool available = (p_mask & (1u << i)) != 0;
|
||||||
|
Button *button = channel_buttons[i];
|
||||||
|
button->set_visible(available);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorChannelSelector::on_channel_button_toggled(bool p_unused_pressed) {
|
||||||
|
emit_signal("selected_channels_changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ColorChannelSelector::get_selected_channels_mask() const {
|
||||||
|
uint32_t mask = 0;
|
||||||
|
for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) {
|
||||||
|
Button *button = channel_buttons[i];
|
||||||
|
if (button->is_visible() && channel_buttons[i]->is_pressed()) {
|
||||||
|
mask |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
Vector4 ColorChannelSelector::get_selected_channel_factors() const {
|
||||||
|
Vector4 channel_factors;
|
||||||
|
const uint32_t mask = get_selected_channels_mask();
|
||||||
|
for (unsigned int i = 0; i < 4; ++i) {
|
||||||
|
if ((mask & (1 << i)) != 0) {
|
||||||
|
channel_factors[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return channel_factors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorChannelSelector::create_button(unsigned int p_channel_index, const String &p_text, Control *p_parent) {
|
||||||
|
ERR_FAIL_COND(p_channel_index >= CHANNEL_COUNT);
|
||||||
|
ERR_FAIL_COND(channel_buttons[p_channel_index] != nullptr);
|
||||||
|
Button *button = memnew(Button);
|
||||||
|
button->set_text(p_text);
|
||||||
|
button->set_toggle_mode(true);
|
||||||
|
button->set_pressed(true);
|
||||||
|
|
||||||
|
// Don't show focus, it stands out too much and remains visible which can be confusing.
|
||||||
|
button->add_theme_style_override("focus", memnew(StyleBoxEmpty));
|
||||||
|
|
||||||
|
// Make it look similar to toolbar buttons.
|
||||||
|
button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||||
|
|
||||||
|
button->connect(SceneStringName(toggled), callable_mp(this, &ColorChannelSelector::on_channel_button_toggled));
|
||||||
|
p_parent->add_child(button);
|
||||||
|
channel_buttons[p_channel_index] = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorChannelSelector::on_toggled(bool p_pressed) {
|
||||||
|
panel->set_visible(p_pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorChannelSelector::_bind_methods() {
|
||||||
|
ADD_SIGNAL(MethodInfo("selected_channels_changed"));
|
||||||
|
}
|
65
editor/plugins/color_channel_selector.h
Normal file
65
editor/plugins/color_channel_selector.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/**************************************************************************/
|
||||||
|
/* color_channel_selector.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. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef COLOR_CHANNEL_SELECTOR_H
|
||||||
|
#define COLOR_CHANNEL_SELECTOR_H
|
||||||
|
|
||||||
|
#include "scene/gui/box_container.h"
|
||||||
|
|
||||||
|
class PanelContainer;
|
||||||
|
class Button;
|
||||||
|
|
||||||
|
class ColorChannelSelector : public HBoxContainer {
|
||||||
|
GDCLASS(ColorChannelSelector, HBoxContainer);
|
||||||
|
|
||||||
|
static const unsigned int CHANNEL_COUNT = 4;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ColorChannelSelector();
|
||||||
|
|
||||||
|
void set_available_channels_mask(uint32_t p_mask);
|
||||||
|
uint32_t get_selected_channels_mask() const;
|
||||||
|
Vector4 get_selected_channel_factors() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _notification(int p_what);
|
||||||
|
|
||||||
|
void on_channel_button_toggled(bool p_unused_pressed);
|
||||||
|
void create_button(unsigned int p_channel_index, const String &p_text, Control *p_parent);
|
||||||
|
void on_toggled(bool p_pressed);
|
||||||
|
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
Button *channel_buttons[CHANNEL_COUNT] = {};
|
||||||
|
PanelContainer *panel = nullptr;
|
||||||
|
Button *toggle_button = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COLOR_CHANNEL_SELECTOR_H
|
|
@ -31,6 +31,7 @@
|
||||||
#include "texture_3d_editor_plugin.h"
|
#include "texture_3d_editor_plugin.h"
|
||||||
|
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
|
#include "editor/plugins/color_channel_selector.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "scene/gui/label.h"
|
#include "scene/gui/label.h"
|
||||||
|
|
||||||
|
@ -44,8 +45,29 @@ constexpr const char *texture_3d_shader = R"(
|
||||||
uniform sampler3D tex;
|
uniform sampler3D tex;
|
||||||
uniform float layer;
|
uniform float layer;
|
||||||
|
|
||||||
|
uniform vec4 u_channel_factors = vec4(1.0);
|
||||||
|
|
||||||
|
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
|
||||||
|
// Filter RGB.
|
||||||
|
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
|
||||||
|
|
||||||
|
// Remove transparency when alpha is not enabled.
|
||||||
|
output_color.a = mix(1.0, output_color.a, factors.a);
|
||||||
|
|
||||||
|
// Switch to opaque grayscale when visualizing only one channel.
|
||||||
|
float csum = factors.r + factors.g + factors.b + factors.a;
|
||||||
|
float single = clamp(2.0 - csum, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float c = input_color[i];
|
||||||
|
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_color;
|
||||||
|
}
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
COLOR = textureLod(tex, vec3(UV, layer), 0.0);
|
COLOR = textureLod(tex, vec3(UV, layer), 0.0);
|
||||||
|
COLOR = filter_preview_colors(COLOR, u_channel_factors);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -94,6 +116,8 @@ void Texture3DEditor::_update_material(bool p_texture_changed) {
|
||||||
if (p_texture_changed) {
|
if (p_texture_changed) {
|
||||||
material->set_shader_parameter("tex", texture->get_rid());
|
material->set_shader_parameter("tex", texture->get_rid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
material->set_shader_parameter("u_channel_factors", channel_selector->get_selected_channel_factors());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture3DEditor::_make_shaders() {
|
void Texture3DEditor::_make_shaders() {
|
||||||
|
@ -138,30 +162,44 @@ void Texture3DEditor::_update_gui() {
|
||||||
|
|
||||||
layer->set_max(texture->get_depth() - 1);
|
layer->set_max(texture->get_depth() - 1);
|
||||||
|
|
||||||
const String format = Image::get_format_name(texture->get_format());
|
const Image::Format format = texture->get_format();
|
||||||
|
const String format_name = Image::get_format_name(format);
|
||||||
|
|
||||||
if (texture->has_mipmaps()) {
|
if (texture->has_mipmaps()) {
|
||||||
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), texture->get_format());
|
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);
|
||||||
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), true) * texture->get_depth();
|
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_depth();
|
||||||
|
|
||||||
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
|
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
texture->get_depth(),
|
texture->get_depth(),
|
||||||
format,
|
format_name,
|
||||||
mip_count,
|
mip_count,
|
||||||
String::humanize_size(memory)));
|
String::humanize_size(memory)));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), false) * texture->get_depth();
|
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_depth();
|
||||||
|
|
||||||
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
|
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
texture->get_depth(),
|
texture->get_depth(),
|
||||||
format,
|
format_name,
|
||||||
String::humanize_size(memory)));
|
String::humanize_size(memory)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32_t components_mask = Image::get_format_component_mask(format);
|
||||||
|
if (is_power_of_2(components_mask)) {
|
||||||
|
// Only one channel available, no point in showing a channel selector.
|
||||||
|
channel_selector->hide();
|
||||||
|
} else {
|
||||||
|
channel_selector->show();
|
||||||
|
channel_selector->set_available_channels_mask(components_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture3DEditor::on_selected_channels_changed() {
|
||||||
|
_update_material(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
|
void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
|
||||||
|
@ -215,6 +253,11 @@ Texture3DEditor::Texture3DEditor() {
|
||||||
|
|
||||||
add_child(layer);
|
add_child(layer);
|
||||||
|
|
||||||
|
channel_selector = memnew(ColorChannelSelector);
|
||||||
|
channel_selector->connect("selected_channels_changed", callable_mp(this, &Texture3DEditor::on_selected_channels_changed));
|
||||||
|
channel_selector->set_anchors_preset(Control::PRESET_TOP_LEFT);
|
||||||
|
add_child(channel_selector);
|
||||||
|
|
||||||
info = memnew(Label);
|
info = memnew(Label);
|
||||||
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
|
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
|
||||||
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
|
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
#include "scene/resources/shader.h"
|
#include "scene/resources/shader.h"
|
||||||
#include "scene/resources/texture.h"
|
#include "scene/resources/texture.h"
|
||||||
|
|
||||||
|
class ColorChannelSelector;
|
||||||
|
|
||||||
class Texture3DEditor : public Control {
|
class Texture3DEditor : public Control {
|
||||||
GDCLASS(Texture3DEditor, Control);
|
GDCLASS(Texture3DEditor, Control);
|
||||||
|
|
||||||
|
@ -49,6 +51,8 @@ class Texture3DEditor : public Control {
|
||||||
|
|
||||||
Control *texture_rect = nullptr;
|
Control *texture_rect = nullptr;
|
||||||
|
|
||||||
|
ColorChannelSelector *channel_selector = nullptr;
|
||||||
|
|
||||||
bool setting = false;
|
bool setting = false;
|
||||||
|
|
||||||
void _make_shaders();
|
void _make_shaders();
|
||||||
|
@ -67,6 +71,8 @@ class Texture3DEditor : public Control {
|
||||||
void _update_material(bool p_texture_changed);
|
void _update_material(bool p_texture_changed);
|
||||||
void _update_gui();
|
void _update_gui();
|
||||||
|
|
||||||
|
void on_selected_channels_changed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "texture_editor_plugin.h"
|
#include "texture_editor_plugin.h"
|
||||||
|
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
|
#include "editor/plugins/color_channel_selector.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "scene/gui/aspect_ratio_container.h"
|
#include "scene/gui/aspect_ratio_container.h"
|
||||||
#include "scene/gui/color_rect.h"
|
#include "scene/gui/color_rect.h"
|
||||||
|
@ -41,6 +42,36 @@
|
||||||
#include "scene/resources/compressed_texture.h"
|
#include "scene/resources/compressed_texture.h"
|
||||||
#include "scene/resources/image_texture.h"
|
#include "scene/resources/image_texture.h"
|
||||||
#include "scene/resources/portable_compressed_texture.h"
|
#include "scene/resources/portable_compressed_texture.h"
|
||||||
|
#include "scene/resources/style_box_flat.h"
|
||||||
|
|
||||||
|
constexpr const char *texture_2d_shader = R"(
|
||||||
|
shader_type canvas_item;
|
||||||
|
render_mode blend_mix;
|
||||||
|
|
||||||
|
uniform vec4 u_channel_factors = vec4(1.0);
|
||||||
|
|
||||||
|
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
|
||||||
|
// Filter RGB.
|
||||||
|
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
|
||||||
|
|
||||||
|
// Remove transparency when alpha is not enabled.
|
||||||
|
output_color.a = mix(1.0, output_color.a, factors.a);
|
||||||
|
|
||||||
|
// Switch to opaque grayscale when visualizing only one channel.
|
||||||
|
float csum = factors.r + factors.g + factors.b + factors.a;
|
||||||
|
float single = clamp(2.0 - csum, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float c = input_color[i];
|
||||||
|
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
COLOR = filter_preview_colors(texture(TEXTURE, UV), u_channel_factors);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
TextureRect *TexturePreview::get_texture_display() {
|
TextureRect *TexturePreview::get_texture_display() {
|
||||||
return texture_display;
|
return texture_display;
|
||||||
|
@ -72,8 +103,8 @@ void TexturePreview::_notification(int p_what) {
|
||||||
|
|
||||||
void TexturePreview::_draw_outline() {
|
void TexturePreview::_draw_outline() {
|
||||||
const float outline_width = Math::round(EDSCALE);
|
const float outline_width = Math::round(EDSCALE);
|
||||||
const Rect2 outline_rect = Rect2(Vector2(), texture_display->get_size()).grow(outline_width * 0.5);
|
const Rect2 outline_rect = Rect2(Vector2(), outline_overlay->get_size()).grow(outline_width * 0.5);
|
||||||
texture_display->draw_rect(outline_rect, cached_outline_color, false, outline_width);
|
outline_overlay->draw_rect(outline_rect, cached_outline_color, false, outline_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TexturePreview::_update_texture_display_ratio() {
|
void TexturePreview::_update_texture_display_ratio() {
|
||||||
|
@ -82,25 +113,49 @@ void TexturePreview::_update_texture_display_ratio() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TexturePreview::_update_metadata_label_text() {
|
static Image::Format get_texture_2d_format(const Ref<Texture2D> &p_texture) {
|
||||||
const Ref<Texture2D> texture = texture_display->get_texture();
|
const Ref<ImageTexture> image_texture = p_texture;
|
||||||
|
if (image_texture.is_valid()) {
|
||||||
String format;
|
return image_texture->get_format();
|
||||||
if (Object::cast_to<ImageTexture>(*texture)) {
|
|
||||||
format = Image::get_format_name(Object::cast_to<ImageTexture>(*texture)->get_format());
|
|
||||||
} else if (Object::cast_to<CompressedTexture2D>(*texture)) {
|
|
||||||
format = Image::get_format_name(Object::cast_to<CompressedTexture2D>(*texture)->get_format());
|
|
||||||
} else {
|
|
||||||
format = texture->get_class();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Ref<Image> image = texture->get_image();
|
const Ref<CompressedTexture2D> compressed_texture = p_texture;
|
||||||
|
if (compressed_texture.is_valid()) {
|
||||||
|
return compressed_texture->get_format();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtlasTexture?
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
return Image::FORMAT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_texture_mipmaps_count(const Ref<Texture2D> &p_texture) {
|
||||||
|
ERR_FAIL_COND_V(p_texture.is_null(), -1);
|
||||||
|
// We are having to download the image only to get its mipmaps count. It would be nice if we didn't have to.
|
||||||
|
Ref<Image> image = p_texture->get_image();
|
||||||
if (image.is_valid()) {
|
if (image.is_valid()) {
|
||||||
const int mipmaps = image->get_mipmap_count();
|
return image->get_mipmap_count();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexturePreview::_update_metadata_label_text() {
|
||||||
|
const Ref<Texture2D> texture = texture_display->get_texture();
|
||||||
|
ERR_FAIL_COND(texture.is_null());
|
||||||
|
|
||||||
|
const Image::Format format = get_texture_2d_format(texture.ptr());
|
||||||
|
|
||||||
|
const String format_name = format != Image::FORMAT_MAX ? Image::get_format_name(format) : texture->get_class();
|
||||||
|
|
||||||
|
const Vector2i resolution = texture->get_size();
|
||||||
|
const int mipmaps = get_texture_mipmaps_count(texture);
|
||||||
|
|
||||||
|
if (format != Image::FORMAT_MAX) {
|
||||||
// Avoid signed integer overflow that could occur with huge texture sizes by casting everything to uint64_t.
|
// Avoid signed integer overflow that could occur with huge texture sizes by casting everything to uint64_t.
|
||||||
uint64_t memory = uint64_t(image->get_width()) * uint64_t(image->get_height()) * uint64_t(Image::get_format_pixel_size(image->get_format()));
|
uint64_t memory = uint64_t(resolution.x) * uint64_t(resolution.y) * uint64_t(Image::get_format_pixel_size(format));
|
||||||
// Handle VRAM-compressed formats that are stored with 4 bpp.
|
// Handle VRAM-compressed formats that are stored with 4 bpp.
|
||||||
memory >>= Image::get_format_pixel_rshift(image->get_format());
|
memory >>= Image::get_format_pixel_rshift(format);
|
||||||
|
|
||||||
float mipmaps_multiplier = 1.0;
|
float mipmaps_multiplier = 1.0;
|
||||||
float mipmap_increase = 0.25;
|
float mipmap_increase = 0.25;
|
||||||
|
@ -117,7 +172,7 @@ void TexturePreview::_update_metadata_label_text() {
|
||||||
vformat(String::utf8("%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
|
vformat(String::utf8("%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
format,
|
format_name,
|
||||||
mipmaps,
|
mipmaps,
|
||||||
String::humanize_size(memory)));
|
String::humanize_size(memory)));
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,7 +182,7 @@ void TexturePreview::_update_metadata_label_text() {
|
||||||
vformat(String::utf8("%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
|
vformat(String::utf8("%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
format,
|
format_name,
|
||||||
String::humanize_size(memory)));
|
String::humanize_size(memory)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,10 +190,14 @@ void TexturePreview::_update_metadata_label_text() {
|
||||||
vformat(String::utf8("%d×%d %s"),
|
vformat(String::utf8("%d×%d %s"),
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
format));
|
format_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TexturePreview::on_selected_channels_changed() {
|
||||||
|
material->set_shader_parameter("u_channel_factors", channel_selector->get_selected_channel_factors());
|
||||||
|
}
|
||||||
|
|
||||||
TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
|
TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
|
||||||
set_custom_minimum_size(Size2(0.0, 256.0) * EDSCALE);
|
set_custom_minimum_size(Size2(0.0, 256.0) * EDSCALE);
|
||||||
|
|
||||||
|
@ -163,19 +222,48 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
|
||||||
checkerboard->set_texture_repeat(CanvasItem::TEXTURE_REPEAT_ENABLED);
|
checkerboard->set_texture_repeat(CanvasItem::TEXTURE_REPEAT_ENABLED);
|
||||||
centering_container->add_child(checkerboard);
|
centering_container->add_child(checkerboard);
|
||||||
|
|
||||||
|
{
|
||||||
|
Ref<Shader> shader;
|
||||||
|
shader.instantiate();
|
||||||
|
shader->set_code(texture_2d_shader);
|
||||||
|
|
||||||
|
material.instantiate();
|
||||||
|
material->set_shader(shader);
|
||||||
|
material->set_shader_parameter("u_channel_factors", Vector4(1, 1, 1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
texture_display = memnew(TextureRect);
|
texture_display = memnew(TextureRect);
|
||||||
texture_display->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
|
texture_display->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
|
||||||
texture_display->set_texture(p_texture);
|
texture_display->set_texture(p_texture);
|
||||||
texture_display->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
texture_display->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||||
|
texture_display->set_material(material);
|
||||||
centering_container->add_child(texture_display);
|
centering_container->add_child(texture_display);
|
||||||
|
|
||||||
texture_display->connect(SceneStringName(draw), callable_mp(this, &TexturePreview::_draw_outline));
|
// Creating a separate control so it is not affected by the filtering shader.
|
||||||
|
outline_overlay = memnew(Control);
|
||||||
|
centering_container->add_child(outline_overlay);
|
||||||
|
|
||||||
|
outline_overlay->connect(SceneStringName(draw), callable_mp(this, &TexturePreview::_draw_outline));
|
||||||
|
|
||||||
if (p_texture.is_valid()) {
|
if (p_texture.is_valid()) {
|
||||||
_update_texture_display_ratio();
|
_update_texture_display_ratio();
|
||||||
p_texture->connect_changed(callable_mp(this, &TexturePreview::_update_texture_display_ratio));
|
p_texture->connect_changed(callable_mp(this, &TexturePreview::_update_texture_display_ratio));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Null can be passed by `Camera3DPreview` (which immediately after sets a texture anyways).
|
||||||
|
const Image::Format format = p_texture.is_valid() ? get_texture_2d_format(p_texture.ptr()) : Image::FORMAT_MAX;
|
||||||
|
const uint32_t components_mask = format != Image::FORMAT_MAX ? Image::get_format_component_mask(format) : 0xf;
|
||||||
|
|
||||||
|
// Add color channel selector at the bottom left if more than 1 channel is available.
|
||||||
|
if (p_show_metadata && !is_power_of_2(components_mask)) {
|
||||||
|
channel_selector = memnew(ColorChannelSelector);
|
||||||
|
channel_selector->connect("selected_channels_changed", callable_mp(this, &TexturePreview::on_selected_channels_changed));
|
||||||
|
channel_selector->set_h_size_flags(Control::SIZE_SHRINK_BEGIN);
|
||||||
|
channel_selector->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
|
||||||
|
channel_selector->set_available_channels_mask(components_mask);
|
||||||
|
add_child(channel_selector);
|
||||||
|
}
|
||||||
|
|
||||||
if (p_show_metadata) {
|
if (p_show_metadata) {
|
||||||
metadata_label = memnew(Label);
|
metadata_label = memnew(Label);
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
class AspectRatioContainer;
|
class AspectRatioContainer;
|
||||||
class ColorRect;
|
class ColorRect;
|
||||||
class TextureRect;
|
class TextureRect;
|
||||||
|
class ShaderMaterial;
|
||||||
|
class ColorChannelSelector;
|
||||||
|
|
||||||
class TexturePreview : public MarginContainer {
|
class TexturePreview : public MarginContainer {
|
||||||
GDCLASS(TexturePreview, MarginContainer);
|
GDCLASS(TexturePreview, MarginContainer);
|
||||||
|
@ -47,10 +49,14 @@ private:
|
||||||
TextureRect *texture_display = nullptr;
|
TextureRect *texture_display = nullptr;
|
||||||
|
|
||||||
MarginContainer *margin_container = nullptr;
|
MarginContainer *margin_container = nullptr;
|
||||||
|
Control *outline_overlay = nullptr;
|
||||||
AspectRatioContainer *centering_container = nullptr;
|
AspectRatioContainer *centering_container = nullptr;
|
||||||
ColorRect *bg_rect = nullptr;
|
ColorRect *bg_rect = nullptr;
|
||||||
TextureRect *checkerboard = nullptr;
|
TextureRect *checkerboard = nullptr;
|
||||||
Label *metadata_label = nullptr;
|
Label *metadata_label = nullptr;
|
||||||
|
Ref<ShaderMaterial> material;
|
||||||
|
|
||||||
|
ColorChannelSelector *channel_selector = nullptr;
|
||||||
|
|
||||||
Color cached_outline_color;
|
Color cached_outline_color;
|
||||||
|
|
||||||
|
@ -61,6 +67,8 @@ protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
void _update_texture_display_ratio();
|
void _update_texture_display_ratio();
|
||||||
|
|
||||||
|
void on_selected_channels_changed();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TextureRect *get_texture_display();
|
TextureRect *get_texture_display();
|
||||||
TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata);
|
TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "texture_layered_editor_plugin.h"
|
#include "texture_layered_editor_plugin.h"
|
||||||
|
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
|
#include "editor/plugins/color_channel_selector.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "scene/gui/label.h"
|
#include "scene/gui/label.h"
|
||||||
|
|
||||||
|
@ -43,9 +44,29 @@ constexpr const char *array_2d_shader = R"(
|
||||||
|
|
||||||
uniform sampler2DArray tex;
|
uniform sampler2DArray tex;
|
||||||
uniform float layer;
|
uniform float layer;
|
||||||
|
uniform vec4 u_channel_factors = vec4(1.0);
|
||||||
|
|
||||||
|
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
|
||||||
|
// Filter RGB.
|
||||||
|
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
|
||||||
|
|
||||||
|
// Remove transparency when alpha is not enabled.
|
||||||
|
output_color.a = mix(1.0, output_color.a, factors.a);
|
||||||
|
|
||||||
|
// Switch to opaque grayscale when visualizing only one channel.
|
||||||
|
float csum = factors.r + factors.g + factors.b + factors.a;
|
||||||
|
float single = clamp(2.0 - csum, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float c = input_color[i];
|
||||||
|
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_color;
|
||||||
|
}
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
COLOR = textureLod(tex, vec3(UV, layer), 0.0);
|
COLOR = textureLod(tex, vec3(UV, layer), 0.0);
|
||||||
|
COLOR = filter_preview_colors(COLOR, u_channel_factors);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -58,9 +79,30 @@ constexpr const char *cubemap_shader = R"(
|
||||||
uniform vec3 normal;
|
uniform vec3 normal;
|
||||||
uniform mat3 rot;
|
uniform mat3 rot;
|
||||||
|
|
||||||
|
uniform vec4 u_channel_factors = vec4(1.0);
|
||||||
|
|
||||||
|
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
|
||||||
|
// Filter RGB.
|
||||||
|
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
|
||||||
|
|
||||||
|
// Remove transparency when alpha is not enabled.
|
||||||
|
output_color.a = mix(1.0, output_color.a, factors.a);
|
||||||
|
|
||||||
|
// Switch to opaque grayscale when visualizing only one channel.
|
||||||
|
float csum = factors.r + factors.g + factors.b + factors.a;
|
||||||
|
float single = clamp(2.0 - csum, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float c = input_color[i];
|
||||||
|
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_color;
|
||||||
|
}
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));
|
vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));
|
||||||
COLOR = textureLod(tex, n, 0.0);
|
COLOR = textureLod(tex, n, 0.0);
|
||||||
|
COLOR = filter_preview_colors(COLOR, u_channel_factors);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -73,9 +115,30 @@ constexpr const char *cubemap_array_shader = R"(
|
||||||
uniform mat3 rot;
|
uniform mat3 rot;
|
||||||
uniform float layer;
|
uniform float layer;
|
||||||
|
|
||||||
|
uniform vec4 u_channel_factors = vec4(1.0);
|
||||||
|
|
||||||
|
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
|
||||||
|
// Filter RGB.
|
||||||
|
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
|
||||||
|
|
||||||
|
// Remove transparency when alpha is not enabled.
|
||||||
|
output_color.a = mix(1.0, output_color.a, factors.a);
|
||||||
|
|
||||||
|
// Switch to opaque grayscale when visualizing only one channel.
|
||||||
|
float csum = factors.r + factors.g + factors.b + factors.a;
|
||||||
|
float single = clamp(2.0 - csum, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float c = input_color[i];
|
||||||
|
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_color;
|
||||||
|
}
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));
|
vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));
|
||||||
COLOR = textureLod(tex, vec4(n, layer), 0.0);
|
COLOR = textureLod(tex, vec4(n, layer), 0.0);
|
||||||
|
COLOR = filter_preview_colors(COLOR, u_channel_factors);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -102,7 +165,8 @@ void TextureLayeredEditor::_update_gui() {
|
||||||
|
|
||||||
_texture_rect_update_area();
|
_texture_rect_update_area();
|
||||||
|
|
||||||
const String format = Image::get_format_name(texture->get_format());
|
const Image::Format format = texture->get_format();
|
||||||
|
const String format_name = Image::get_format_name(format);
|
||||||
String texture_info;
|
String texture_info;
|
||||||
|
|
||||||
switch (texture->get_layered_type()) {
|
switch (texture->get_layered_type()) {
|
||||||
|
@ -113,7 +177,7 @@ void TextureLayeredEditor::_update_gui() {
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
texture->get_layers(),
|
texture->get_layers(),
|
||||||
format);
|
format_name);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case TextureLayered::LAYERED_TYPE_CUBEMAP: {
|
case TextureLayered::LAYERED_TYPE_CUBEMAP: {
|
||||||
|
@ -122,7 +186,7 @@ void TextureLayeredEditor::_update_gui() {
|
||||||
texture_info = vformat(String::utf8("%d×%d %s\n"),
|
texture_info = vformat(String::utf8("%d×%d %s\n"),
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
format);
|
format_name);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY: {
|
case TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY: {
|
||||||
|
@ -132,7 +196,7 @@ void TextureLayeredEditor::_update_gui() {
|
||||||
texture->get_width(),
|
texture->get_width(),
|
||||||
texture->get_height(),
|
texture->get_height(),
|
||||||
texture->get_layers() / 6,
|
texture->get_layers() / 6,
|
||||||
format);
|
format_name);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -141,21 +205,30 @@ void TextureLayeredEditor::_update_gui() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture->has_mipmaps()) {
|
if (texture->has_mipmaps()) {
|
||||||
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), texture->get_format());
|
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);
|
||||||
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), true) * texture->get_layers();
|
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_layers();
|
||||||
|
|
||||||
texture_info += vformat(TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
|
texture_info += vformat(TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
|
||||||
mip_count,
|
mip_count,
|
||||||
String::humanize_size(memory));
|
String::humanize_size(memory));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), texture->get_format(), false) * texture->get_layers();
|
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_layers();
|
||||||
|
|
||||||
texture_info += vformat(TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
|
texture_info += vformat(TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
|
||||||
String::humanize_size(memory));
|
String::humanize_size(memory));
|
||||||
}
|
}
|
||||||
|
|
||||||
info->set_text(texture_info);
|
info->set_text(texture_info);
|
||||||
|
|
||||||
|
const uint32_t components_mask = Image::get_format_component_mask(format);
|
||||||
|
if (is_power_of_2(components_mask)) {
|
||||||
|
// Only one channel available, no point in showing a channel selector.
|
||||||
|
channel_selector->hide();
|
||||||
|
} else {
|
||||||
|
channel_selector->show();
|
||||||
|
channel_selector->set_available_channels_mask(components_mask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureLayeredEditor::_notification(int p_what) {
|
void TextureLayeredEditor::_notification(int p_what) {
|
||||||
|
@ -212,6 +285,15 @@ void TextureLayeredEditor::_update_material(bool p_texture_changed) {
|
||||||
if (p_texture_changed) {
|
if (p_texture_changed) {
|
||||||
materials[texture->get_layered_type()]->set_shader_parameter("tex", texture->get_rid());
|
materials[texture->get_layered_type()]->set_shader_parameter("tex", texture->get_rid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Vector4 channel_factors = channel_selector->get_selected_channel_factors();
|
||||||
|
for (unsigned int i = 0; i < 3; ++i) {
|
||||||
|
materials[i]->set_shader_parameter("u_channel_factors", channel_factors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureLayeredEditor::on_selected_channels_changed() {
|
||||||
|
_update_material(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureLayeredEditor::_make_shaders() {
|
void TextureLayeredEditor::_make_shaders() {
|
||||||
|
@ -309,6 +391,11 @@ TextureLayeredEditor::TextureLayeredEditor() {
|
||||||
|
|
||||||
add_child(layer);
|
add_child(layer);
|
||||||
|
|
||||||
|
channel_selector = memnew(ColorChannelSelector);
|
||||||
|
channel_selector->connect("selected_channels_changed", callable_mp(this, &TextureLayeredEditor::on_selected_channels_changed));
|
||||||
|
channel_selector->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
|
||||||
|
add_child(channel_selector);
|
||||||
|
|
||||||
info = memnew(Label);
|
info = memnew(Label);
|
||||||
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
|
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
|
||||||
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
|
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
#include "scene/resources/shader.h"
|
#include "scene/resources/shader.h"
|
||||||
#include "scene/resources/texture.h"
|
#include "scene/resources/texture.h"
|
||||||
|
|
||||||
|
class ColorChannelSelector;
|
||||||
|
|
||||||
class TextureLayeredEditor : public Control {
|
class TextureLayeredEditor : public Control {
|
||||||
GDCLASS(TextureLayeredEditor, Control);
|
GDCLASS(TextureLayeredEditor, Control);
|
||||||
|
|
||||||
|
@ -53,6 +55,8 @@ class TextureLayeredEditor : public Control {
|
||||||
|
|
||||||
bool setting = false;
|
bool setting = false;
|
||||||
|
|
||||||
|
ColorChannelSelector *channel_selector = nullptr;
|
||||||
|
|
||||||
void _make_shaders();
|
void _make_shaders();
|
||||||
void _update_material(bool p_texture_changed);
|
void _update_material(bool p_texture_changed);
|
||||||
|
|
||||||
|
@ -69,6 +73,8 @@ class TextureLayeredEditor : public Control {
|
||||||
|
|
||||||
void _update_gui();
|
void _update_gui();
|
||||||
|
|
||||||
|
void on_selected_channels_changed();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||||
|
|
Loading…
Reference in a new issue