2016-06-18 14:46:12 +02:00
/**************************************************************************/
/* editor_preview_plugins.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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2015-05-31 01:59:42 -03:00
# include "editor_preview_plugins.h"
2017-01-16 08:04:19 +01:00
2022-02-12 02:46:22 +01:00
# include "core/config/project_settings.h"
2024-10-16 14:13:36 +03:00
# include "core/io/image.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
2023-09-06 21:02:52 +02:00
# include "core/object/script_language.h"
2025-05-31 16:57:51 +08:00
# include "editor/editor_node.h"
2022-02-14 14:00:03 +01:00
# include "editor/editor_paths.h"
2017-03-05 14:21:25 +01:00
# include "editor/editor_settings.h"
2024-01-15 13:14:55 +01:00
# include "editor/themes/editor_scale.h"
2025-05-31 16:57:51 +08:00
# include "main/main.h"
# include "modules/gridmap/grid_map.h"
# include "scene/2d/animated_sprite_2d.h"
# include "scene/2d/camera_2d.h"
# include "scene/2d/line_2d.h"
# include "scene/2d/mesh_instance_2d.h"
# include "scene/2d/multimesh_instance_2d.h"
# include "scene/2d/physics/touch_screen_button.h"
# include "scene/2d/polygon_2d.h"
# include "scene/2d/sprite_2d.h"
# include "scene/2d/tile_map_layer.h"
# include "scene/3d/cpu_particles_3d.h"
# include "scene/3d/gpu_particles_3d.h"
# include "scene/3d/light_3d.h"
# include "scene/3d/mesh_instance_3d.h"
# include "scene/gui/option_button.h"
# include "scene/main/viewport.h"
# include "scene/main/window.h"
2023-07-11 22:29:09 +02:00
# include "scene/resources/atlas_texture.h"
2019-02-12 17:18:13 +01:00
# include "scene/resources/bit_map.h"
2020-09-03 14:22:16 +03:00
# include "scene/resources/font.h"
2023-07-11 22:29:09 +02:00
# include "scene/resources/gradient_texture.h"
# include "scene/resources/image_texture.h"
2017-08-26 17:46:49 +02:00
# include "scene/resources/material.h"
2015-05-31 21:13:24 -03:00
# include "scene/resources/mesh.h"
2025-05-31 16:57:51 +08:00
# include "scene/resources/world_2d.h"
2019-12-24 15:17:23 +08:00
# include "servers/audio/audio_stream.h"
2016-07-06 19:04:21 +02:00
2018-02-28 20:23:40 +01:00
void post_process_preview ( Ref < Image > p_image ) {
2020-05-14 16:41:43 +02:00
if ( p_image - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
2017-12-30 13:38:51 +01:00
p_image - > convert ( Image : : FORMAT_RGBA8 ) ;
2020-05-14 16:41:43 +02:00
}
2017-12-30 13:38:51 +01:00
const int w = p_image - > get_width ( ) ;
const int h = p_image - > get_height ( ) ;
const int r = MIN ( w , h ) / 32 ;
const int r2 = r * r ;
Color transparent = Color ( 0 , 0 , 0 , 0 ) ;
for ( int i = 0 ; i < r ; i + + ) {
for ( int j = 0 ; j < r ; j + + ) {
int dx = i - r ;
int dy = j - r ;
if ( dx * dx + dy * dy > r2 ) {
p_image - > set_pixel ( i , j , transparent ) ;
p_image - > set_pixel ( w - 1 - i , j , transparent ) ;
p_image - > set_pixel ( w - 1 - i , h - 1 - j , transparent ) ;
p_image - > set_pixel ( i , h - 1 - j , transparent ) ;
} else {
break ;
}
}
}
}
2017-06-09 00:23:50 -03:00
bool EditorTexturePreviewPlugin : : handles ( const String & p_type ) const {
2024-12-10 14:57:10 +01:00
return ClassDB : : is_parent_class ( p_type , " Texture " ) ;
2015-05-31 01:59:42 -03:00
}
2019-05-20 10:45:12 +02:00
bool EditorTexturePreviewPlugin : : generate_small_preview_automatically ( ) const {
2018-09-12 13:10:49 +02:00
return true ;
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorTexturePreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2017-06-09 00:23:50 -03:00
Ref < Image > img ;
2024-07-21 22:11:43 +02:00
Ref < AtlasTexture > tex_atlas = p_from ;
Ref < Texture3D > tex_3d = p_from ;
Ref < TextureLayered > tex_lyr = p_from ;
if ( tex_atlas . is_valid ( ) ) {
Ref < Texture2D > tex = tex_atlas - > get_atlas ( ) ;
2024-08-25 14:15:10 +02:00
if ( tex . is_null ( ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2016-01-19 04:27:27 +01:00
}
2019-02-27 00:44:09 +00:00
2021-03-28 12:32:17 +01:00
Ref < Image > atlas = tex - > get_image ( ) ;
2024-08-25 14:15:10 +02:00
if ( atlas . is_null ( ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2019-02-27 00:44:09 +00:00
}
2025-02-08 08:11:35 +11:00
if ( atlas - > is_compressed ( ) ) {
atlas = atlas - > duplicate ( ) ;
if ( atlas - > decompress ( ) ! = OK ) {
return Ref < Texture2D > ( ) ;
}
}
2024-07-21 22:11:43 +02:00
if ( ! tex_atlas - > get_region ( ) . has_area ( ) ) {
return Ref < Texture2D > ( ) ;
}
img = atlas - > get_region ( tex_atlas - > get_region ( ) ) ;
} else if ( tex_3d . is_valid ( ) ) {
if ( tex_3d - > get_depth ( ) = = 0 ) {
return Ref < Texture2D > ( ) ;
}
Vector < Ref < Image > > data = tex_3d - > get_data ( ) ;
2024-10-08 17:56:17 +02:00
if ( data . size ( ) ! = tex_3d - > get_depth ( ) ) {
return Ref < Texture2D > ( ) ;
}
// Use the middle slice for the thumbnail.
const int mid_depth = ( tex_3d - > get_depth ( ) - 1 ) / 2 ;
2024-07-21 22:11:43 +02:00
if ( ! data . is_empty ( ) & & data [ mid_depth ] . is_valid ( ) ) {
img = data [ mid_depth ] - > duplicate ( ) ;
}
} else if ( tex_lyr . is_valid ( ) ) {
if ( tex_lyr - > get_layers ( ) = = 0 ) {
2024-06-18 12:09:15 +08:00
return Ref < Texture2D > ( ) ;
}
2024-10-08 17:56:17 +02:00
// Use the middle slice for the thumbnail.
2024-07-21 22:11:43 +02:00
const int mid_layer = ( tex_lyr - > get_layers ( ) - 1 ) / 2 ;
Ref < Image > data = tex_lyr - > get_layer_data ( mid_layer ) ;
if ( data . is_valid ( ) ) {
img = data - > duplicate ( ) ;
}
2017-06-09 00:23:50 -03:00
} else {
2019-06-11 15:43:37 -03:00
Ref < Texture2D > tex = p_from ;
2019-11-10 09:49:13 +01:00
if ( tex . is_valid ( ) ) {
2021-03-28 12:32:17 +01:00
img = tex - > get_image ( ) ;
2019-11-10 09:49:13 +01:00
if ( img . is_valid ( ) ) {
img = img - > duplicate ( ) ;
}
2019-01-27 13:39:16 -03:00
}
2016-01-19 04:27:27 +01:00
}
2020-12-15 12:04:21 +00:00
if ( img . is_null ( ) | | img - > is_empty ( ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2024-07-21 22:11:43 +02:00
2022-08-19 18:14:57 +02:00
p_metadata [ " dimensions " ] = img - > get_size ( ) ;
2015-05-31 01:59:42 -03:00
2017-06-09 00:23:50 -03:00
img - > clear_mipmaps ( ) ;
2015-05-31 01:59:42 -03:00
2017-06-09 00:23:50 -03:00
if ( img - > is_compressed ( ) ) {
2020-05-14 16:41:43 +02:00
if ( img - > decompress ( ) ! = OK ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-06-09 00:23:50 -03:00
} else if ( img - > get_format ( ) ! = Image : : FORMAT_RGB8 & & img - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2015-05-31 01:59:42 -03:00
}
2018-09-12 13:10:49 +02:00
Vector2 new_size = img - > get_size ( ) ;
if ( new_size . x > p_size . x ) {
new_size = Vector2 ( p_size . x , new_size . y * p_size . x / new_size . x ) ;
2015-05-31 01:59:42 -03:00
}
2018-09-12 13:10:49 +02:00
if ( new_size . y > p_size . y ) {
new_size = Vector2 ( new_size . x * p_size . y / new_size . y , p_size . y ) ;
}
2024-03-03 14:37:52 +01:00
Vector2i new_size_i = Vector2i ( new_size ) . maxi ( 1 ) ;
2020-06-18 16:38:45 +08:00
img - > resize ( new_size_i . x , new_size_i . y , Image : : INTERPOLATE_CUBIC ) ;
2017-12-30 13:38:51 +01:00
post_process_preview ( img ) ;
2015-05-31 01:59:42 -03:00
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2015-05-31 01:59:42 -03:00
}
2016-01-03 17:14:28 -03:00
////////////////////////////////////////////////////////////////////////////
2018-07-29 16:45:23 -03:00
bool EditorImagePreviewPlugin : : handles ( const String & p_type ) const {
return p_type = = " Image " ;
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorImagePreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2018-07-29 16:45:23 -03:00
Ref < Image > img = p_from ;
2020-12-15 12:04:21 +00:00
if ( img . is_null ( ) | | img - > is_empty ( ) ) {
2018-07-29 16:45:23 -03:00
return Ref < Image > ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 16:45:23 -03:00
img = img - > duplicate ( ) ;
img - > clear_mipmaps ( ) ;
if ( img - > is_compressed ( ) ) {
2020-05-14 16:41:43 +02:00
if ( img - > decompress ( ) ! = OK ) {
2018-07-29 16:45:23 -03:00
return Ref < Image > ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 16:45:23 -03:00
} else if ( img - > get_format ( ) ! = Image : : FORMAT_RGB8 & & img - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
img - > convert ( Image : : FORMAT_RGBA8 ) ;
}
2018-09-12 13:10:49 +02:00
Vector2 new_size = img - > get_size ( ) ;
if ( new_size . x > p_size . x ) {
new_size = Vector2 ( p_size . x , new_size . y * p_size . x / new_size . x ) ;
}
if ( new_size . y > p_size . y ) {
new_size = Vector2 ( new_size . x * p_size . y / new_size . y , p_size . y ) ;
2018-07-29 16:45:23 -03:00
}
2018-09-12 13:10:49 +02:00
img - > resize ( new_size . x , new_size . y , Image : : INTERPOLATE_CUBIC ) ;
2018-07-29 16:45:23 -03:00
post_process_preview ( img ) ;
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2018-07-29 16:45:23 -03:00
}
2019-05-20 10:45:12 +02:00
bool EditorImagePreviewPlugin : : generate_small_preview_automatically ( ) const {
2018-09-12 13:10:49 +02:00
return true ;
}
2020-05-14 14:29:06 +02:00
2018-07-29 16:45:23 -03:00
////////////////////////////////////////////////////////////////////////////
2022-02-06 14:12:19 +01:00
2017-06-09 00:23:50 -03:00
bool EditorBitmapPreviewPlugin : : handles ( const String & p_type ) const {
return ClassDB : : is_parent_class ( p_type , " BitMap " ) ;
2016-01-03 17:14:28 -03:00
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorBitmapPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2017-06-09 00:23:50 -03:00
Ref < BitMap > bm = p_from ;
2016-01-03 17:14:28 -03:00
2017-06-09 00:23:50 -03:00
if ( bm - > get_size ( ) = = Size2 ( ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2016-01-03 17:14:28 -03:00
}
2020-02-17 18:06:54 -03:00
Vector < uint8_t > data ;
2016-01-03 17:14:28 -03:00
2017-06-09 00:23:50 -03:00
data . resize ( bm - > get_size ( ) . width * bm - > get_size ( ) . height ) ;
2016-01-03 17:14:28 -03:00
{
2020-02-17 18:06:54 -03:00
uint8_t * w = data . ptrw ( ) ;
2016-01-03 17:14:28 -03:00
2017-06-09 00:23:50 -03:00
for ( int i = 0 ; i < bm - > get_size ( ) . width ; i + + ) {
for ( int j = 0 ; j < bm - > get_size ( ) . height ; j + + ) {
2022-09-01 18:39:17 +02:00
if ( bm - > get_bit ( i , j ) ) {
2020-02-17 18:06:54 -03:00
w [ j * ( int ) bm - > get_size ( ) . width + i ] = 255 ;
2016-01-03 17:14:28 -03:00
} else {
2020-02-17 18:06:54 -03:00
w [ j * ( int ) bm - > get_size ( ) . width + i ] = 0 ;
2016-01-03 17:14:28 -03:00
}
}
}
}
2022-07-22 20:06:19 +02:00
Ref < Image > img = Image : : create_from_data ( bm - > get_size ( ) . width , bm - > get_size ( ) . height , false , Image : : FORMAT_L8 , data ) ;
2016-01-03 17:14:28 -03:00
2017-06-09 00:23:50 -03:00
if ( img - > is_compressed ( ) ) {
2020-05-14 16:41:43 +02:00
if ( img - > decompress ( ) ! = OK ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-06-09 00:23:50 -03:00
} else if ( img - > get_format ( ) ! = Image : : FORMAT_RGB8 & & img - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2016-01-03 17:14:28 -03:00
}
2018-09-12 13:10:49 +02:00
Vector2 new_size = img - > get_size ( ) ;
if ( new_size . x > p_size . x ) {
new_size = Vector2 ( p_size . x , new_size . y * p_size . x / new_size . x ) ;
}
if ( new_size . y > p_size . y ) {
new_size = Vector2 ( new_size . x * p_size . y / new_size . y , p_size . y ) ;
2016-01-03 17:14:28 -03:00
}
2018-09-12 13:10:49 +02:00
img - > resize ( new_size . x , new_size . y , Image : : INTERPOLATE_CUBIC ) ;
2017-12-30 13:38:51 +01:00
post_process_preview ( img ) ;
2016-01-03 17:14:28 -03:00
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2016-01-03 17:14:28 -03:00
}
2019-05-20 10:45:12 +02:00
bool EditorBitmapPreviewPlugin : : generate_small_preview_automatically ( ) const {
2018-09-12 13:10:49 +02:00
return true ;
}
2015-05-31 01:59:42 -03:00
///////////////////////////////////////////////////////////////////////////
2025-05-31 16:57:51 +08:00
void EditorPackedScenePreviewPlugin : : abort ( ) {
draw_requester . abort ( ) ;
aborted = true ;
}
2017-06-09 00:23:50 -03:00
bool EditorPackedScenePreviewPlugin : : handles ( const String & p_type ) const {
return ClassDB : : is_parent_class ( p_type , " PackedScene " ) ;
}
2019-06-11 15:43:37 -03:00
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorPackedScenePreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
return generate_from_path ( p_from - > get_path ( ) , p_size , p_metadata ) ;
2017-06-09 00:23:50 -03:00
}
2015-05-31 01:59:42 -03:00
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorPackedScenePreviewPlugin : : generate_from_path ( const String & p_path , const Size2 & p_size , Dictionary & p_metadata ) const {
2025-05-31 16:57:51 +08:00
ERR_FAIL_COND_V_MSG ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) , Ref < Texture2D > ( ) , " This function can only be called from the editor. " ) ;
ERR_FAIL_NULL_V_MSG ( EditorNode : : get_singleton ( ) , Ref < Texture2D > ( ) , " EditorNode doesn't exist. " ) ;
2015-05-31 01:59:42 -03:00
2025-05-31 16:57:51 +08:00
// Lower abort flag
aborted = false ;
2015-05-31 01:59:42 -03:00
2025-05-31 16:57:51 +08:00
Error load_error ;
2025-06-14 18:11:37 +08:00
Ref < PackedScene > pack = ResourceLoader : : load ( p_path , " PackedScene " , ResourceFormatLoader : : CACHE_MODE_IGNORE_DEEP , & load_error ) ; // no more cache issues?
2025-05-31 16:57:51 +08:00
if ( load_error ! = OK ) {
print_error ( vformat ( " Failed to generate scene thumbnail for %s : Loaded with error code %d " , p_path , int ( load_error ) ) ) ;
return Ref < Texture2D > ( ) ;
}
if ( pack . is_null ( ) ) {
print_error ( vformat ( " Failed to generate scene thumbnail for %s : Invalid scene file " , p_path ) ) ;
return Ref < Texture2D > ( ) ;
}
2015-05-31 01:59:42 -03:00
2025-05-31 16:57:51 +08:00
bool _scene_setup_success = _setup_packed_scene ( pack ) ; // We don't want tool scripts to fire off when generating previews
if ( ! _scene_setup_success ) {
print_error ( vformat ( " Failed to generate scene thumbnail for %s : error in setting up preview scene, thus not safe to create thumbnail image " , p_path ) ) ;
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-05-31 01:59:42 -03:00
2025-05-31 16:57:51 +08:00
Node * p_scene = pack - > instantiate ( ) ; // The instantiated preview scene
2015-05-31 01:59:42 -03:00
2025-05-31 16:57:51 +08:00
// Prohibit Viewport class as root when generating thumbnails
if ( Object : : cast_to < Viewport > ( p_scene ) ) {
p_scene - > queue_free ( ) ;
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2017-06-09 00:23:50 -03:00
}
2025-05-31 16:57:51 +08:00
int count_2d = 0 ;
int count_3d = 0 ;
int count_light_3d = 0 ;
_count_node_types ( p_scene , count_2d , count_3d , count_light_3d ) ;
if ( count_3d > 0 ) { // Is 3d scene
// Setup preview viewport
SubViewport * sub_viewport = memnew ( SubViewport ) ;
sub_viewport - > set_update_mode ( SubViewport : : UpdateMode : : UPDATE_DISABLED ) ;
sub_viewport - > set_size ( Size2i ( Math : : round ( p_size . x ) , Math : : round ( p_size . y ) ) ) ;
sub_viewport - > set_transparent_background ( false ) ;
sub_viewport - > set_disable_3d ( false ) ;
if ( p_size . x < 2048 & & p_size . y < 2048 ) { // Universal baseline for textures in Godot 4 is 4K
sub_viewport - > set_scaling_3d_scale ( 2.0 ) ; // Supersampling x2
}
if ( RS : : get_singleton ( ) - > get_current_rendering_method ( ) ! = " gl_compatibility " ) {
sub_viewport - > set_msaa_3d ( Viewport : : MSAA : : MSAA_8X ) ;
}
Ref < Environment > environment ;
Color default_clear_color = GLOBAL_GET ( " rendering/environment/defaults/default_clear_color " ) ;
environment . instantiate ( ) ;
environment - > set_background ( Environment : : BGMode : : BG_CLEAR_COLOR ) ;
environment - > set_bg_color ( default_clear_color ) ;
Ref < World3D > world_3d ;
sub_viewport - > set_world_3d ( world_3d ) ;
sub_viewport - > set_use_own_world_3d ( true ) ;
Camera3D * camera_3d = memnew ( Camera3D ) ;
sub_viewport - > add_child ( camera_3d ) ;
camera_3d - > set_perspective ( preview_3d_fov , 0.05 , 10000.0 ) ;
// Add scene to viewport
_setup_scene_3d ( p_scene ) ;
sub_viewport - > add_child ( p_scene ) ;
// Setup preview light
DirectionalLight3D * light_1 = nullptr ;
DirectionalLight3D * light_2 = nullptr ;
if ( count_light_3d = = 0 ) {
light_1 = memnew ( DirectionalLight3D ) ;
light_2 = memnew ( DirectionalLight3D ) ;
sub_viewport - > add_child ( light_1 ) ;
sub_viewport - > add_child ( light_2 ) ;
light_1 - > set_color ( Color ( 1.0 , 1.0 , 1.0 , 1.0 ) ) ;
light_2 - > set_color ( Color ( 0.7 , 0.7 , 0.7 , 1.0 ) ) ;
light_1 - > set_transform ( Transform3D ( Basis ( ) . rotated ( Vector3 ( 0 , 1 , 0 ) , - Math : : PI / 6 ) , Vector3 ( 0.0 , 0.0 , 0.0 ) ) ) ;
light_2 - > set_transform ( Transform3D ( Basis ( ) . rotated ( Vector3 ( 1 , 0 , 0 ) , - Math : : PI / 6 ) , Vector3 ( 0.0 , 0.0 , 0.0 ) ) ) ;
}
// Setup preview camera
AABB scene_aabb ;
_calculate_scene_aabb ( p_scene , scene_aabb ) ;
float bound_sphere_radius = ( scene_aabb . get_end ( ) - scene_aabb . get_position ( ) ) . length ( ) / 2.0f ;
if ( bound_sphere_radius < = 0.0f ) {
// The scene has zero volume, so just it give a literal
bound_sphere_radius = 1.0f ;
}
float cam_distance = bound_sphere_radius / Math : : tan ( Math : : deg_to_rad ( preview_3d_fov ) / 2.0f ) ;
Transform3D thumbnail_cam_trans_3d ;
thumbnail_cam_trans_3d . set_origin ( scene_aabb . get_center ( ) + Vector3 ( 1.0f , 0.25f , 1.0f ) . normalized ( ) * cam_distance ) ;
thumbnail_cam_trans_3d . set_look_at ( thumbnail_cam_trans_3d . origin , scene_aabb . get_center ( ) ) ;
// Set camera to orthogonal if distance exceeds camera default far (large scene)
if ( thumbnail_cam_trans_3d . origin . length ( ) > camera_3d - > get_far ( ) ) {
real_t distance = thumbnail_cam_trans_3d . origin . length ( ) ;
camera_3d - > set_orthogonal ( distance / 2.0 , distance / 1000.0 , distance ) ; // Approximately contains the whole scene
}
camera_3d - > set_transform ( thumbnail_cam_trans_3d ) ;
// Attach the preview viewport to MainTree
sub_viewport - > set_update_mode ( SubViewport : : UpdateMode : : UPDATE_ONCE ) ;
if ( EditorResourcePreview : : get_singleton ( ) - > is_threaded ( ) ) {
callable_mp ( ( Node * ) EditorNode : : get_singleton ( ) , & Node : : add_child ) . call_deferred ( sub_viewport , false , Node : : InternalMode : : INTERNAL_MODE_DISABLED ) ;
callable_mp ( camera_3d , & Camera3D : : set_current ) . call_deferred ( true ) ;
} else {
EditorNode : : get_singleton ( ) - > add_child ( sub_viewport ) ;
camera_3d - > set_current ( true ) ;
}
_wait_frame ( ) ;
draw_requester . request_and_wait ( sub_viewport - > get_viewport_rid ( ) ) ;
if ( aborted ) {
sub_viewport - > queue_free ( ) ;
return Ref < Texture2D > ( ) ;
}
// Retrieve thumbnail image
Ref < ImageTexture > thumbnail ;
thumbnail = ImageTexture : : create_from_image ( sub_viewport - > get_texture ( ) - > get_image ( ) ) ;
// Clean up
sub_viewport - > queue_free ( ) ;
return thumbnail ;
}
if ( count_2d > 0 ) { // Is 2d scene
// If anyone want to rewrite this part to call RenderingServer directly, note that at the time of writing,
// there's a limitation where CanvasItem cannot be rendered outside of the tree.
// See CanvasItem::queue_redraw() and RenderingServer::draw()
int texture_filter = GLOBAL_GET ( " rendering/textures/canvas_textures/default_texture_filter " ) ;
int texture_repeat = GLOBAL_GET ( " rendering/textures/canvas_textures/default_texture_repeat " ) ;
SubViewport * sub_viewport = memnew ( SubViewport ) ;
sub_viewport - > set_update_mode ( SubViewport : : UpdateMode : : UPDATE_DISABLED ) ;
sub_viewport - > set_disable_3d ( true ) ;
sub_viewport - > set_transparent_background ( false ) ;
if ( RS : : get_singleton ( ) - > get_current_rendering_method ( ) ! = " gl_compatibility " ) {
sub_viewport - > set_msaa_2d ( Viewport : : MSAA : : MSAA_8X ) ;
}
sub_viewport - > set_default_canvas_item_texture_filter ( Viewport : : DefaultCanvasItemTextureFilter ( texture_filter ) ) ;
sub_viewport - > set_default_canvas_item_texture_repeat ( Viewport : : DefaultCanvasItemTextureRepeat ( texture_repeat ) ) ;
Ref < World2D > world ;
world . instantiate ( ) ;
sub_viewport - > set_world_2d ( world ) ;
sub_viewport - > add_child ( p_scene ) ;
_setup_scene_2d ( p_scene ) ;
_hide_gui_in_scene ( p_scene ) ;
// Preview camera
Camera2D * camera = memnew ( Camera2D ) ;
sub_viewport - > add_child ( camera ) ;
camera - > set_enabled ( true ) ;
// Attach subviewport (following process needs scene to be in tree)
if ( EditorResourcePreview : : get_singleton ( ) - > is_threaded ( ) ) {
callable_mp ( ( Node * ) EditorNode : : get_singleton ( ) , & Node : : add_child ) . call_deferred ( sub_viewport , false , Node : : InternalMode : : INTERNAL_MODE_DISABLED ) ;
callable_mp ( ( Camera2D * ) camera , & Camera2D : : make_current ) . call_deferred ( ) ;
} else {
EditorNode : : get_singleton ( ) - > add_child ( sub_viewport ) ;
camera - > make_current ( ) ;
}
_wait_frame ( ) ;
if ( aborted ) {
sub_viewport - > queue_free ( ) ;
return Ref < Texture2D > ( ) ;
}
// Calculate scene rect
Rect2 scene_rect ;
_calculate_scene_rect ( p_scene , scene_rect ) ;
Vector2 scene_true_center = scene_rect . get_center ( ) ;
// Place camera 2D
camera - > set_position ( Point2 ( scene_true_center ) ) ;
// Render viewport
uint16_t scene_rect_long = MAX ( scene_rect . get_size ( ) . x , scene_rect . get_size ( ) . y ) ;
if ( scene_rect_long = = 0 ) {
scene_rect_long = MAX ( p_size . x , p_size . y ) ; // Prevent 0 size rect (which causes error) and defaults to thumbnail size.
}
sub_viewport - > set_size ( p_size ) ;
camera - > set_zoom ( Vector2 ( p_size . x / float ( scene_rect_long ) , p_size . y / float ( scene_rect_long ) ) ) ;
sub_viewport - > set_update_mode ( SubViewport : : UpdateMode : : UPDATE_ONCE ) ;
_wait_frame ( ) ;
draw_requester . request_and_wait ( sub_viewport - > get_viewport_rid ( ) ) ;
if ( aborted ) {
sub_viewport - > queue_free ( ) ;
return Ref < Texture2D > ( ) ;
}
// Retrieve thumbnail of 2D (No GUI)
Ref < ImageTexture > capture_2d = ImageTexture : : create_from_image ( sub_viewport - > get_texture ( ) - > get_image ( ) ) ;
capture_2d - > get_image ( ) - > resize ( p_size . x , p_size . y ) ;
capture_2d - > get_image ( ) - > convert ( Image : : Format : : FORMAT_RGBA8 ) ; // ALPHA channel is required for image blending
// Prepare for gui render
callable_mp ( ( Node * ) sub_viewport , & Node : : remove_child ) . call_deferred ( p_scene ) ;
p_scene - > queue_free ( ) ;
p_scene = pack - > instantiate ( ) ;
_setup_scene_2d ( p_scene ) ;
_hide_node_2d_in_scene ( p_scene ) ;
SubViewport * sub_viewport_gui = memnew ( SubViewport ) ;
sub_viewport_gui - > set_size ( Size2i ( GLOBAL_GET ( " display/window/size/viewport_width " ) , GLOBAL_GET ( " display/window/size/viewport_height " ) ) ) ;
sub_viewport_gui - > set_update_mode ( SubViewport : : UpdateMode : : UPDATE_DISABLED ) ;
sub_viewport_gui - > set_transparent_background ( true ) ;
if ( RS : : get_singleton ( ) - > get_current_rendering_method ( ) ! = " gl_compatibility " ) {
sub_viewport_gui - > set_msaa_2d ( Viewport : : MSAA : : MSAA_8X ) ;
}
sub_viewport_gui - > set_default_canvas_item_texture_filter ( Viewport : : DefaultCanvasItemTextureFilter ( texture_filter ) ) ;
sub_viewport_gui - > set_default_canvas_item_texture_repeat ( Viewport : : DefaultCanvasItemTextureRepeat ( texture_repeat ) ) ;
sub_viewport_gui - > set_disable_3d ( true ) ;
sub_viewport_gui - > add_child ( p_scene ) ;
// Render GUI
sub_viewport_gui - > set_update_mode ( SubViewport : : UpdateMode : : UPDATE_ONCE ) ;
if ( EditorResourcePreview : : get_singleton ( ) - > is_threaded ( ) ) {
callable_mp ( ( Node * ) EditorNode : : get_singleton ( ) , & Node : : add_child ) . call_deferred ( sub_viewport_gui , false , Node : : InternalMode : : INTERNAL_MODE_DISABLED ) ;
} else {
EditorNode : : get_singleton ( ) - > add_child ( sub_viewport_gui ) ;
}
_wait_frame ( ) ;
draw_requester . request_and_wait ( sub_viewport_gui - > get_viewport_rid ( ) ) ;
if ( aborted ) {
sub_viewport - > queue_free ( ) ;
sub_viewport_gui - > queue_free ( ) ;
return Ref < Texture2D > ( ) ;
}
// Retrieve thumbnail of gui
Ref < ImageTexture > capture_gui = ImageTexture : : create_from_image ( sub_viewport_gui - > get_texture ( ) - > get_image ( ) ) ;
capture_gui - > get_image ( ) - > resize ( p_size . x , p_size . y ) ;
// Mix 2D, GUI thumbnail images into one
Ref < ImageTexture > thumbnail ;
thumbnail . instantiate ( ) ;
Ref < Image > thumbnail_image = Image : : create_empty ( p_size . x , p_size . y , false , Image : : Format : : FORMAT_RGBA8 ) ; // blend_rect needs ALPHA channel to work
thumbnail_image - > blend_rect ( capture_2d - > get_image ( ) , capture_2d - > get_image ( ) - > get_used_rect ( ) , Point2i ( 0 , 0 ) ) ;
thumbnail_image - > blend_rect ( capture_gui - > get_image ( ) , capture_gui - > get_image ( ) - > get_used_rect ( ) , Point2i ( 0 , 0 ) ) ;
thumbnail - > set_image ( thumbnail_image ) ;
// Clean up
sub_viewport - > queue_free ( ) ;
sub_viewport_gui - > queue_free ( ) ;
return thumbnail ;
}
// Is scene without any visuals (No Node2D, Node3D, Control found)
return Ref < Texture2D > ( ) ;
}
void EditorPackedScenePreviewPlugin : : _setup_scene_3d ( Node * p_node ) const {
// Do not account any SubViewport at preview scene, as it would not render correctly
if ( Object : : cast_to < SubViewport > ( p_node ) & & p_node - > get_parent ( ) ) {
p_node - > get_parent ( ) - > remove_child ( p_node ) ;
callable_mp ( p_node , & Node : : queue_free ) . call_deferred ( ) ;
return ;
}
// Don't let window to popup
Window * window = Object : : cast_to < Window > ( p_node ) ;
if ( window ) {
window - > set_visible ( false ) ;
}
// Make sure no Node2D, Control node is visible (Might occupy large proportion of the thumbnail)
Node2D * n2d = Object : : cast_to < Node2D > ( p_node ) ;
if ( n2d ) {
n2d - > set_visible ( false ) ;
}
Control * ctrl = Object : : cast_to < Control > ( p_node ) ;
if ( ctrl ) {
ctrl - > set_visible ( false ) ;
}
// Disable skinning for skeleton meshes (animations and skeleton scaling might disturb aabb calculation)
MeshInstance3D * mesh = Object : : cast_to < MeshInstance3D > ( p_node ) ;
if ( mesh & & mesh - > is_visible_in_tree ( ) ) {
mesh - > set_skeleton_path ( NodePath ( ) ) ;
}
CPUParticles3D * cpu_particles = Object : : cast_to < CPUParticles3D > ( p_node ) ;
if ( cpu_particles & & cpu_particles - > is_visible_in_tree ( ) ) {
cpu_particles - > set_pre_process_time ( cpu_particles - > get_lifetime ( ) * 0.5 ) ; // Fast forward the particle emission to make it render something
cpu_particles - > set_use_local_coordinates ( true ) ; // HACK - Now constructs scene outside of tree, using global coords will cause error, this may introduce visual bugs, but is the best solution now
cpu_particles - > restart ( true ) ; // Keep seed to make simulation persistent
}
GPUParticles3D * gpu_particles = Object : : cast_to < GPUParticles3D > ( p_node ) ;
if ( gpu_particles & & gpu_particles - > is_visible_in_tree ( ) ) {
// Convert to CPUParticles (As GPUParticles can't be rendered correctly in thumbnails, don't know why)
CPUParticles3D * gtc_particles = memnew ( CPUParticles3D ) ; // GPU to CPU particles
gtc_particles - > convert_from_particles ( gpu_particles ) ;
gpu_particles - > add_child ( gtc_particles ) ; // So the created CPUParticles instance will be freed later we call queue_free() on the preview scene
// Setup CPUParticles
gtc_particles - > set_pre_process_time ( gtc_particles - > get_lifetime ( ) * 0.5 ) ;
gtc_particles - > set_use_local_coordinates ( true ) ;
gtc_particles - > restart ( true ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_setup_scene_3d ( p_node - > get_child ( i ) ) ;
}
}
void EditorPackedScenePreviewPlugin : : _setup_scene_2d ( Node * p_node ) const {
// Do not account any SubViewport at preview scene, as it would not render correctly
if ( Object : : cast_to < SubViewport > ( p_node ) & & p_node - > get_parent ( ) ) {
p_node - > get_parent ( ) - > remove_child ( p_node ) ;
callable_mp ( p_node , & Node : : queue_free ) . call_deferred ( ) ;
return ;
}
// Don't let window to popup
Window * window = Object : : cast_to < Window > ( p_node ) ;
if ( window ) {
window - > set_visible ( false ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_setup_scene_2d ( p_node - > get_child ( i ) ) ;
}
}
void EditorPackedScenePreviewPlugin : : _count_node_types ( Node * p_node , int & r_c2d , int & r_c3d , int & r_clight3d ) const {
if ( p_node - > is_class ( " Control " ) | | p_node - > is_class ( " Node2D " ) ) {
r_c2d + + ;
}
if ( p_node - > is_class ( " Node3D " ) ) {
r_c3d + + ;
}
if ( p_node - > is_class ( " Light3D " ) ) {
r_clight3d + + ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
// Do not count anything under SubViewport node, those children are irrelevant for determining if the scene is 2D or 3D.
if ( p_node - > get_child ( i ) - > is_class ( " SubViewport " ) ) {
continue ;
}
_count_node_types ( p_node - > get_child ( i ) , r_c2d , r_c3d , r_clight3d ) ;
}
}
void EditorPackedScenePreviewPlugin : : _calculate_scene_rect ( Node * p_node , Rect2 & r_rect ) const {
// NOTE: There's no universal way to get the exact global rect as a Node2D, so we dig into subclasses one by one
// NOTE:
// 1. Sprite2D::position by default is at the **center** of the sprite. (with offset == (0,0) AND centered == true)
// 2. Rect2::position is at the **up-left** of the rect
// 3. AABB::position is at the **bottom-left-forward** of the bounding box
//
// calculation below is done with these in mind.
Rect2 n2d_rect ; // The rect of the current iterating Node2D
Sprite2D * sprite = Object : : cast_to < Sprite2D > ( p_node ) ;
if ( sprite & & sprite - > is_visible_in_tree ( ) ) {
n2d_rect . size = sprite - > get_global_scale ( ) * sprite - > get_rect ( ) . size ;
n2d_rect . position = sprite - > get_global_position ( ) + sprite - > get_offset ( ) * sprite - > get_global_scale ( ) ;
if ( sprite - > is_centered ( ) ) {
n2d_rect . position - = n2d_rect . size / 2.0f ;
}
}
AnimatedSprite2D * anim_sprite = Object : : cast_to < AnimatedSprite2D > ( p_node ) ;
if ( anim_sprite & & anim_sprite - > is_visible_in_tree ( ) ) {
if ( anim_sprite - > get_sprite_frames ( ) . is_valid ( ) ) {
Ref < Texture2D > current_frame_tex = anim_sprite - > get_sprite_frames ( ) - > get_frame_texture ( anim_sprite - > get_animation ( ) , anim_sprite - > get_frame ( ) ) ;
if ( current_frame_tex . is_valid ( ) ) {
n2d_rect . size = current_frame_tex - > get_size ( ) * anim_sprite - > get_global_scale ( ) ;
n2d_rect . position = anim_sprite - > get_global_position ( ) + anim_sprite - > get_offset ( ) * anim_sprite - > get_global_scale ( ) ;
if ( anim_sprite - > is_centered ( ) ) {
n2d_rect . position - = n2d_rect . size / 2.0f ;
}
}
}
}
MeshInstance2D * mesh2d = Object : : cast_to < MeshInstance2D > ( p_node ) ;
if ( mesh2d & & mesh2d - > is_visible_in_tree ( ) ) {
// NOTE: Conversion is 1m = 1px (before 2d scale)
Ref < Mesh > mesh = mesh2d - > get_mesh ( ) ;
if ( mesh . is_valid ( ) ) {
// Discard z axis (depth) and only get length of mesh in x,y axis
n2d_rect . size . x = ( mesh - > get_aabb ( ) . get_end ( ) - mesh - > get_aabb ( ) . position ) . x ;
n2d_rect . size . y = ( mesh - > get_aabb ( ) . get_end ( ) - mesh - > get_aabb ( ) . position ) . y ;
n2d_rect . size * = mesh2d - > get_global_scale ( ) ;
// Account for mesh offset in 3d space when calculating rect2
n2d_rect . position . x = mesh2d - > get_global_position ( ) . x + mesh - > get_aabb ( ) . position . x * mesh2d - > get_global_scale ( ) . x ; // AABB::position is bottom-left
n2d_rect . position . y = mesh2d - > get_global_position ( ) . y + mesh - > get_aabb ( ) . position . y * mesh2d - > get_global_scale ( ) . y ;
}
}
MultiMeshInstance2D * mmesh2d = Object : : cast_to < MultiMeshInstance2D > ( p_node ) ;
if ( mmesh2d & & mmesh2d - > is_visible_in_tree ( ) ) {
// Basically the same procedure as MeshInstance2D.
Ref < MultiMesh > mmesh = mmesh2d - > get_multimesh ( ) ;
if ( mmesh . is_valid ( ) ) {
n2d_rect . size . x = ( mmesh - > get_aabb ( ) . get_end ( ) - mmesh - > get_aabb ( ) . position ) . x ;
n2d_rect . size . y = ( mmesh - > get_aabb ( ) . get_end ( ) - mmesh - > get_aabb ( ) . position ) . y ;
n2d_rect . size * = mmesh2d - > get_global_scale ( ) ;
n2d_rect . position . x = mmesh2d - > get_global_position ( ) . x + mmesh - > get_aabb ( ) . position . x * mmesh2d - > get_global_scale ( ) . x ;
n2d_rect . position . y = mmesh2d - > get_global_position ( ) . y + mmesh - > get_aabb ( ) . position . y * mmesh2d - > get_global_scale ( ) . y ;
}
}
TileMapLayer * tile_map = Object : : cast_to < TileMapLayer > ( p_node ) ;
if ( tile_map & & tile_map - > is_visible_in_tree ( ) ) {
// NOTE: TileMapLayer::get_used_rect() only count cells, not their actual pixel size
if ( tile_map - > get_tile_set ( ) . is_valid ( ) ) {
Size2 tile_size = tile_map - > get_tile_set ( ) - > get_tile_size ( ) ; // Tile map cell pixel size (x,y).
Rect2 tile_rect = tile_map - > get_used_rect ( ) ; // Unit is in cells, not pixels!
n2d_rect . position = tile_map - > get_global_position ( ) + tile_rect . position * tile_size * tile_map - > get_global_scale ( ) ; // Accounts tilemap offset
n2d_rect . size = tile_rect . size * tile_size * tile_map - > get_global_scale ( ) ;
}
}
Polygon2D * poly2d = Object : : cast_to < Polygon2D > ( p_node ) ;
if ( poly2d & & poly2d - > is_visible_in_tree ( ) ) {
PackedVector2Array polygon = poly2d - > get_polygon ( ) ;
if ( polygon . size ( ) > 2 ) { // Abort if there's no surface (min = 3 verts)
// Calculate bounds
float max_x = polygon [ 0 ] . x ;
float min_x = polygon [ 0 ] . x ;
float max_y = polygon [ 0 ] . y ;
float min_y = polygon [ 0 ] . y ;
for ( int i = 0 ; i < polygon . size ( ) ; i + + ) {
if ( polygon [ i ] . x > max_x ) {
max_x = polygon [ i ] . x ;
}
if ( polygon [ i ] . x < min_x ) {
min_x = polygon [ i ] . x ;
}
if ( polygon [ i ] . y > max_y ) {
max_y = polygon [ i ] . y ;
}
if ( polygon [ i ] . y < min_y ) {
min_y = polygon [ i ] . y ;
}
}
Rect2 poly_rect = Rect2 ( min_x , min_y , max_x - min_x , max_y - min_y ) ;
n2d_rect . position = poly2d - > get_global_position ( ) + poly2d - > get_offset ( ) * poly2d - > get_global_scale ( ) ;
n2d_rect . position + = poly_rect . position * poly2d - > get_global_scale ( ) ;
n2d_rect . size = poly_rect . size * poly2d - > get_global_scale ( ) ;
}
}
Line2D * line2d = Object : : cast_to < Line2D > ( p_node ) ;
if ( line2d & & line2d - > is_visible_in_tree ( ) ) {
// The same procedure as Polygon2D
PackedVector2Array points = line2d - > get_points ( ) ;
if ( line2d - > get_point_count ( ) > 1 ) { // Abort if there's no line drawn
// Calculate bounds
float max_x = points [ 0 ] . x ;
float min_x = points [ 0 ] . x ;
float max_y = points [ 0 ] . y ;
float min_y = points [ 0 ] . y ;
for ( int i = 0 ; i < points . size ( ) ; i + + ) {
if ( points [ i ] . x > max_x ) {
max_x = points [ i ] . x ;
}
if ( points [ i ] . x < min_x ) {
min_x = points [ i ] . x ;
}
if ( points [ i ] . y > max_y ) {
max_y = points [ i ] . y ;
}
if ( points [ i ] . y < min_y ) {
min_y = points [ i ] . y ;
}
}
Rect2 line2d_rect = Rect2 ( min_x , min_y , max_x - min_x , max_y - min_y ) ;
n2d_rect . position = line2d - > get_global_position ( ) ;
n2d_rect . position + = line2d_rect . position * line2d - > get_global_scale ( ) ;
n2d_rect . size = line2d_rect . size * line2d - > get_global_scale ( ) ;
n2d_rect . size + = Size2 ( line2d - > get_width ( ) , line2d - > get_width ( ) ) / 2.0f ; // account for line width
}
}
TouchScreenButton * btn = Object : : cast_to < TouchScreenButton > ( p_node ) ;
if ( btn & & btn - > is_visible_in_tree ( ) ) {
Ref < Texture2D > btn_tex = btn - > get_texture_normal ( ) ;
if ( btn_tex . is_valid ( ) ) { // Abort if there's no normal texture for this button (won't display anything)
n2d_rect . position = btn - > get_global_position ( ) ; // It's not possible to offset image in this node
n2d_rect . size = btn_tex - > get_size ( ) * btn - > get_global_scale ( ) ;
}
}
// Merge the calculated node 2d rect
if ( r_rect . get_size ( ) . length_squared ( ) = = 0.0f ) { // Avoid accounting scene origin (0,0) into scene rect.
r_rect = n2d_rect . abs ( ) ;
} else {
r_rect = r_rect . merge ( n2d_rect . abs ( ) ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_calculate_scene_rect ( p_node - > get_child ( i ) , r_rect ) ;
}
}
void EditorPackedScenePreviewPlugin : : _hide_node_2d_in_scene ( Node * p_node ) const {
// NOTE: Irreversible (cannot unhide nodes after this)
// We cannot simple hide() since it will affect all its children (may contain Control nodes)
if ( p_node - > is_class ( " Node2D " ) ) {
Node2D * n2d = Object : : cast_to < Node2D > ( p_node ) ;
n2d - > set_self_modulate ( Color ( 0.0f , 0.0f , 0.0f , 0.0f ) ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_hide_node_2d_in_scene ( p_node - > get_child ( i ) ) ;
}
}
void EditorPackedScenePreviewPlugin : : _hide_gui_in_scene ( Node * p_node ) const {
// NOTE: Irreversible (cannot unhide nodes after this)
// We cannot simply hide() since it will affect all its children (may contain Node2D nodes)
if ( p_node - > is_class ( " Control " ) ) {
Control * ctrl = Object : : cast_to < Control > ( p_node ) ;
ctrl - > set_self_modulate ( Color ( 0.0f , 0.0f , 0.0f , 0.0f ) ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_hide_gui_in_scene ( p_node - > get_child ( i ) ) ;
}
}
// Hacky implementation, remove this when draw_requester.request_and_wait() is sufficient to render previews properly.
// Is needed for 2 reason:
// 1. Scene preview requires adding scene to tree and wait for RenderingServer to redraw.
// 2. To resolve some rendering bugs by waiting extra frames.
void EditorPackedScenePreviewPlugin : : _wait_frame ( ) const {
const uint64_t prev_frame = Engine : : get_singleton ( ) - > get_frames_drawn ( ) ;
while ( Engine : : get_singleton ( ) - > get_frames_drawn ( ) - prev_frame < 1 ) {
if ( ! EditorResourcePreview : : get_singleton ( ) - > is_threaded ( ) ) {
// Is running this on main thread, iterate main loop (or will get stuck here forever)
DisplayServer : : get_singleton ( ) - > process_events ( ) ;
Main : : iteration ( ) ;
}
if ( aborted ) {
break ;
}
continue ;
}
}
void EditorPackedScenePreviewPlugin : : _calculate_scene_aabb ( Node * p_node , AABB & r_aabb ) const {
GeometryInstance3D * g3d = Object : : cast_to < GeometryInstance3D > ( p_node ) ;
if ( g3d & & g3d - > is_visible_in_tree ( ) ) { // Use this because VisualInstance3D may have derived classes that are non-graphical (probes, volumes)
AABB node_aabb = _get_global_transform_3d ( g3d ) . xform ( g3d - > get_aabb ( ) ) ;
r_aabb . merge_with ( node_aabb ) ;
}
CPUParticles3D * cpu_particles = Object : : cast_to < CPUParticles3D > ( p_node ) ;
if ( cpu_particles & & cpu_particles - > is_visible_in_tree ( ) ) { // CPUParticles3D does not calculate particle bounds, so do it here
// Account the furthest position where particles can go
Vector3 particle_destination = _get_global_transform_3d ( cpu_particles ) . origin ;
particle_destination + = cpu_particles - > get_direction ( ) * cpu_particles - > get_param_max ( CPUParticles3D : : PARAM_INITIAL_LINEAR_VELOCITY ) ;
r_aabb . expand_to ( particle_destination * 0.5 ) ;
r_aabb . expand_to ( particle_destination * - 0.5 ) ;
}
GPUParticles3D * gpu_particles = Object : : cast_to < GPUParticles3D > ( p_node ) ;
if ( gpu_particles & & gpu_particles - > is_visible_in_tree ( ) ) {
r_aabb . merge_with ( _get_global_transform_3d ( gpu_particles ) . xform ( gpu_particles - > get_visibility_aabb ( ) ) ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_calculate_scene_aabb ( p_node - > get_child ( i ) , r_aabb ) ;
}
}
Transform3D EditorPackedScenePreviewPlugin : : _get_global_transform_3d ( Node * p_n3d ) const {
// Designed to work even if node is outside the tree (is_inside_tree() != true)
Transform3D global_transform ;
Array parents ;
if ( ! p_n3d - > is_class ( " Node3D " ) ) {
ERR_PRINT ( " Expected a Node3D node as argument " ) ;
return global_transform ;
}
Node * p_loop_node = p_n3d ;
while ( p_loop_node ! = nullptr ) {
if ( p_loop_node - > is_class ( " Node3D " ) ) {
parents . append ( p_loop_node ) ;
}
p_loop_node = p_loop_node - > get_parent ( ) ;
}
parents . reverse ( ) ;
for ( int i = 0 ; i < parents . size ( ) ; i + + ) {
Node3D * p_parent = Object : : cast_to < Node3D > ( parents [ i ] ) ;
if ( i = = 0 ) {
global_transform = p_parent - > get_transform ( ) ;
continue ;
}
global_transform * = p_parent - > get_transform ( ) ;
}
return global_transform ;
}
bool EditorPackedScenePreviewPlugin : : _setup_packed_scene ( Ref < PackedScene > p_pack ) const {
// Refer to SceneState in packed_scene.cpp to see how PackedScene is managed underhood.
// Sanitize
Dictionary bundle = p_pack - > get_state ( ) - > get_bundled_scene ( ) ;
ERR_FAIL_COND_V ( ! bundle . has ( " names " ) , false ) ;
ERR_FAIL_COND_V ( ! bundle . has ( " variants " ) , false ) ;
ERR_FAIL_COND_V ( ! bundle . has ( " node_count " ) , false ) ;
ERR_FAIL_COND_V ( ! bundle . has ( " nodes " ) , false ) ;
ERR_FAIL_COND_V ( ! bundle . has ( " conn_count " ) , false ) ;
ERR_FAIL_COND_V ( ! bundle . has ( " conns " ) , false ) ;
const uint8_t supported_version = 3 ;
uint8_t current_version = bundle . get ( " version " , 1 ) ;
if ( current_version > supported_version ) {
WARN_PRINT_ONCE ( vformat ( " Scene thumbnail creation was built upon PackedScene with version %d, but the version has changed to %d now. " , supported_version , current_version ) ) ;
// And assume it's safe to continue, there should have no reason to change the main structure of PackedScene
}
// Find and remove variants in scene
const Ref < Script > dummy = 0 ;
Array edited_variants = bundle [ " variants " ] ;
if ( edited_variants . is_empty ( ) ) {
return true ; // Scene has no resources at all
}
for ( int i = 0 ; i < edited_variants . size ( ) ; i + + ) {
// Clear script
if ( edited_variants [ i ] . get_type ( ) = = Variant : : OBJECT ) {
if ( Object : : cast_to < Script > ( edited_variants [ i ] ) ) {
edited_variants [ i ] = dummy ;
}
if ( Object : : cast_to < PackedScene > ( edited_variants [ i ] ) ) {
// Recursively apply to all child scenes
_setup_packed_scene ( edited_variants [ i ] ) ;
}
}
// Clear arrays ( Because leaving array of NodePaths is problematic when scripts are removed )
if ( edited_variants [ i ] . get_type ( ) = = Variant : : ARRAY ) {
edited_variants [ i ] = 0 ;
}
}
// Remove signal bindings (As scripts are all removed)
bundle [ " conns " ] = Array ( ) ;
bundle [ " conn_count " ] = Array ( ) ;
// Create a new scene state
bundle [ " variants " ] = edited_variants ;
Ref < SceneState > new_state ;
new_state . instantiate ( ) ;
new_state - > set_bundled_scene ( bundle ) ;
p_pack - > replace_state ( new_state ) ;
return true ;
2015-05-31 01:59:42 -03:00
}
2017-06-09 00:23:50 -03:00
//////////////////////////////////////////////////////////////////
2023-11-10 15:27:51 +01:00
void EditorMaterialPreviewPlugin : : abort ( ) {
2024-02-21 10:44:21 +01:00
draw_requester . abort ( ) ;
2023-11-10 15:27:51 +01:00
}
2017-06-09 00:23:50 -03:00
bool EditorMaterialPreviewPlugin : : handles ( const String & p_type ) const {
2022-02-06 14:12:19 +01:00
return ClassDB : : is_parent_class ( p_type , " Material " ) ; // Any material.
2015-05-31 01:59:42 -03:00
}
2019-05-20 10:45:12 +02:00
bool EditorMaterialPreviewPlugin : : generate_small_preview_automatically ( ) const {
2018-09-12 13:10:49 +02:00
return true ;
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorMaterialPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2015-05-31 01:59:42 -03:00
Ref < Material > material = p_from ;
2019-06-11 15:43:37 -03:00
ERR_FAIL_COND_V ( material . is_null ( ) , Ref < Texture2D > ( ) ) ;
2015-05-31 01:59:42 -03:00
2017-12-06 19:43:22 -03:00
if ( material - > get_shader_mode ( ) = = Shader : : MODE_SPATIAL ) {
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > mesh_surface_set_material ( sphere , 0 , material - > get_rid ( ) ) ;
2015-05-31 01:59:42 -03:00
2024-02-21 10:44:21 +01:00
draw_requester . request_and_wait ( viewport ) ;
2025-05-31 16:57:51 +08:00
if ( EditorResourcePreview : : get_singleton ( ) - > is_threaded ( ) ) {
draw_requester . request_and_wait ( viewport ) ; // HACK - Prevents incorrect thumbnail assignment when using Forward or Mobile renderer, comment out this line to see the bug.
}
2015-05-31 01:59:42 -03:00
2020-03-27 15:21:27 -03:00
Ref < Image > img = RS : : get_singleton ( ) - > texture_2d_get ( viewport_texture ) ;
RS : : get_singleton ( ) - > mesh_surface_set_material ( sphere , 0 , RID ( ) ) ;
2015-05-31 01:59:42 -03:00
2024-08-25 14:15:10 +02:00
ERR_FAIL_COND_V ( img . is_null ( ) , Ref < ImageTexture > ( ) ) ;
2017-12-06 19:43:22 -03:00
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2018-09-12 13:10:49 +02:00
int thumbnail_size = MAX ( p_size . x , p_size . y ) ;
img - > resize ( thumbnail_size , thumbnail_size , Image : : INTERPOLATE_CUBIC ) ;
2017-12-30 13:38:51 +01:00
post_process_preview ( img ) ;
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2017-12-06 19:43:22 -03:00
}
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2015-05-31 01:59:42 -03:00
}
EditorMaterialPreviewPlugin : : EditorMaterialPreviewPlugin ( ) {
2020-03-27 15:21:27 -03:00
scenario = RS : : get_singleton ( ) - > scenario_create ( ) ;
2015-05-31 01:59:42 -03:00
2020-03-27 15:21:27 -03:00
viewport = RS : : get_singleton ( ) - > viewport_create ( ) ;
RS : : get_singleton ( ) - > viewport_set_update_mode ( viewport , RS : : VIEWPORT_UPDATE_DISABLED ) ;
RS : : get_singleton ( ) - > viewport_set_scenario ( viewport , scenario ) ;
RS : : get_singleton ( ) - > viewport_set_size ( viewport , 128 , 128 ) ;
RS : : get_singleton ( ) - > viewport_set_transparent_background ( viewport , true ) ;
RS : : get_singleton ( ) - > viewport_set_active ( viewport , true ) ;
viewport_texture = RS : : get_singleton ( ) - > viewport_get_texture ( viewport ) ;
2015-05-31 01:59:42 -03:00
2020-03-27 15:21:27 -03:00
camera = RS : : get_singleton ( ) - > camera_create ( ) ;
RS : : get_singleton ( ) - > viewport_attach_camera ( viewport , camera ) ;
2020-10-17 01:08:21 -04:00
RS : : get_singleton ( ) - > camera_set_transform ( camera , Transform3D ( Basis ( ) , Vector3 ( 0 , 0 , 3 ) ) ) ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > camera_set_perspective ( camera , 45 , 0.1 , 10 ) ;
2015-05-31 01:59:42 -03:00
2022-07-31 16:20:24 -07:00
if ( GLOBAL_GET ( " rendering/lights_and_shadows/use_physical_light_units " ) ) {
camera_attributes = RS : : get_singleton ( ) - > camera_attributes_create ( ) ;
RS : : get_singleton ( ) - > camera_attributes_set_exposure ( camera_attributes , 1.0 , 0.000032552 ) ; // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
RS : : get_singleton ( ) - > camera_set_camera_attributes ( camera , camera_attributes ) ;
}
2020-03-27 15:21:27 -03:00
light = RS : : get_singleton ( ) - > directional_light_create ( ) ;
light_instance = RS : : get_singleton ( ) - > instance_create2 ( light , scenario ) ;
2020-10-17 01:08:21 -04:00
RS : : get_singleton ( ) - > instance_set_transform ( light_instance , Transform3D ( ) . looking_at ( Vector3 ( - 1 , - 1 , - 1 ) , Vector3 ( 0 , 1 , 0 ) ) ) ;
2015-05-31 01:59:42 -03:00
2020-03-27 15:21:27 -03:00
light2 = RS : : get_singleton ( ) - > directional_light_create ( ) ;
RS : : get_singleton ( ) - > light_set_color ( light2 , Color ( 0.7 , 0.7 , 0.7 ) ) ;
//RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
2017-06-09 00:23:50 -03:00
2020-03-27 15:21:27 -03:00
light_instance2 = RS : : get_singleton ( ) - > instance_create2 ( light2 , scenario ) ;
2015-05-31 01:59:42 -03:00
2020-10-17 01:08:21 -04:00
RS : : get_singleton ( ) - > instance_set_transform ( light_instance2 , Transform3D ( ) . looking_at ( Vector3 ( 0 , 1 , 0 ) , Vector3 ( 0 , 0 , 1 ) ) ) ;
2015-05-31 01:59:42 -03:00
2020-03-27 15:21:27 -03:00
sphere = RS : : get_singleton ( ) - > mesh_create ( ) ;
sphere_instance = RS : : get_singleton ( ) - > instance_create2 ( sphere , scenario ) ;
2015-05-31 01:59:42 -03:00
2017-06-09 00:23:50 -03:00
int lats = 32 ;
int lons = 32 ;
2025-04-10 11:21:05 -05:00
const double lat_step = Math : : TAU / lats ;
const double lon_step = Math : : TAU / lons ;
2020-04-03 05:50:40 -04:00
real_t radius = 1.0 ;
2015-05-31 01:59:42 -03:00
2020-02-17 18:06:54 -03:00
Vector < Vector3 > vertices ;
Vector < Vector3 > normals ;
Vector < Vector2 > uvs ;
2021-08-09 17:15:17 -05:00
Vector < real_t > tangents ;
2025-04-10 11:21:05 -05:00
Basis tt = Basis ( Vector3 ( 0 , 1 , 0 ) , Math : : PI * 0.5 ) ;
2015-05-31 01:59:42 -03:00
2017-06-09 00:23:50 -03:00
for ( int i = 1 ; i < = lats ; i + + ) {
2025-04-10 11:21:05 -05:00
double lat0 = lat_step * ( i - 1 ) - Math : : TAU / 4 ;
2017-06-09 00:23:50 -03:00
double z0 = Math : : sin ( lat0 ) ;
double zr0 = Math : : cos ( lat0 ) ;
2015-05-31 01:59:42 -03:00
2025-04-10 11:21:05 -05:00
double lat1 = lat_step * i - Math : : TAU / 4 ;
2015-05-31 01:59:42 -03:00
double z1 = Math : : sin ( lat1 ) ;
double zr1 = Math : : cos ( lat1 ) ;
2017-06-09 00:23:50 -03:00
for ( int j = lons ; j > = 1 ; j - - ) {
2020-04-03 05:50:40 -04:00
double lng0 = lon_step * ( j - 1 ) ;
2015-05-31 01:59:42 -03:00
double x0 = Math : : cos ( lng0 ) ;
double y0 = Math : : sin ( lng0 ) ;
2020-04-03 05:50:40 -04:00
double lng1 = lon_step * j ;
2015-05-31 01:59:42 -03:00
double x1 = Math : : cos ( lng1 ) ;
double y1 = Math : : sin ( lng1 ) ;
2017-06-09 00:23:50 -03:00
Vector3 v [ 4 ] = {
Vector3 ( x1 * zr0 , z0 , y1 * zr0 ) ,
Vector3 ( x1 * zr1 , z1 , y1 * zr1 ) ,
Vector3 ( x0 * zr1 , z1 , y0 * zr1 ) ,
Vector3 ( x0 * zr0 , z0 , y0 * zr0 )
2015-05-31 01:59:42 -03:00
} ;
# define ADD_POINT(m_idx) \
normals . push_back ( v [ m_idx ] ) ; \
vertices . push_back ( v [ m_idx ] * radius ) ; \
{ \
Vector2 uv ( Math : : atan2 ( v [ m_idx ] . x , v [ m_idx ] . z ) , Math : : atan2 ( - v [ m_idx ] . y , v [ m_idx ] . z ) ) ; \
2025-04-10 11:21:05 -05:00
uv / = Math : : PI ; \
2015-05-31 01:59:42 -03:00
uv * = 4.0 ; \
uv = uv * 0.5 + Vector2 ( 0.5 , 0.5 ) ; \
uvs . push_back ( uv ) ; \
} \
{ \
Vector3 t = tt . xform ( v [ m_idx ] ) ; \
tangents . push_back ( t . x ) ; \
tangents . push_back ( t . y ) ; \
tangents . push_back ( t . z ) ; \
tangents . push_back ( 1.0 ) ; \
}
ADD_POINT ( 0 ) ;
ADD_POINT ( 1 ) ;
ADD_POINT ( 2 ) ;
ADD_POINT ( 2 ) ;
ADD_POINT ( 3 ) ;
ADD_POINT ( 0 ) ;
}
}
Array arr ;
2020-03-27 15:21:27 -03:00
arr . resize ( RS : : ARRAY_MAX ) ;
arr [ RS : : ARRAY_VERTEX ] = vertices ;
arr [ RS : : ARRAY_NORMAL ] = normals ;
arr [ RS : : ARRAY_TANGENT ] = tangents ;
arr [ RS : : ARRAY_TEX_UV ] = uvs ;
RS : : get_singleton ( ) - > mesh_add_surface_from_arrays ( sphere , RS : : PRIMITIVE_TRIANGLES , arr ) ;
2015-05-31 01:59:42 -03:00
}
EditorMaterialPreviewPlugin : : ~ EditorMaterialPreviewPlugin ( ) {
2022-12-12 12:42:37 -05:00
ERR_FAIL_NULL ( RenderingServer : : get_singleton ( ) ) ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > free ( sphere ) ;
RS : : get_singleton ( ) - > free ( sphere_instance ) ;
RS : : get_singleton ( ) - > free ( viewport ) ;
RS : : get_singleton ( ) - > free ( light ) ;
RS : : get_singleton ( ) - > free ( light_instance ) ;
RS : : get_singleton ( ) - > free ( light2 ) ;
RS : : get_singleton ( ) - > free ( light_instance2 ) ;
RS : : get_singleton ( ) - > free ( camera ) ;
2022-07-31 16:20:24 -07:00
RS : : get_singleton ( ) - > free ( camera_attributes ) ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > free ( scenario ) ;
2015-05-31 01:59:42 -03:00
}
///////////////////////////////////////////////////////////////////////////
2017-06-09 00:23:50 -03:00
bool EditorScriptPreviewPlugin : : handles ( const String & p_type ) const {
return ClassDB : : is_parent_class ( p_type , " Script " ) ;
2015-05-31 01:59:42 -03:00
}
2024-01-26 13:03:32 +01:00
Ref < Texture2D > EditorScriptPreviewPlugin : : generate_from_path ( const String & p_path , const Size2 & p_size , Dictionary & p_metadata ) const {
Error err ;
String code = FileAccess : : get_file_as_string ( p_path , & err ) ;
if ( err ! = OK ) {
return Ref < Texture2D > ( ) ;
}
ScriptLanguage * lang = ScriptServer : : get_language_for_extension ( p_path . get_extension ( ) ) ;
return _generate_from_source_code ( lang , code , p_size , p_metadata ) ;
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorScriptPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2015-05-31 01:59:42 -03:00
Ref < Script > scr = p_from ;
2020-05-14 16:41:43 +02:00
if ( scr . is_null ( ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-05-31 01:59:42 -03:00
String code = scr - > get_source_code ( ) . strip_edges ( ) ;
2024-01-26 13:03:32 +01:00
return _generate_from_source_code ( scr - > get_language ( ) , code , p_size , p_metadata ) ;
}
Ref < Texture2D > EditorScriptPreviewPlugin : : _generate_from_source_code ( const ScriptLanguage * p_language , const String & p_source_code , const Size2 & p_size , Dictionary & p_metadata ) const {
if ( p_source_code . is_empty ( ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-05-31 01:59:42 -03:00
2022-05-19 17:00:06 +02:00
HashSet < String > control_flow_keywords ;
HashSet < String > keywords ;
2015-05-31 01:59:42 -03:00
2025-01-15 21:47:43 +08:00
if ( p_language ) {
for ( const String & keyword : p_language - > get_reserved_words ( ) ) {
if ( p_language - > is_control_flow_keyword ( keyword ) ) {
control_flow_keywords . insert ( keyword ) ;
} else {
keywords . insert ( keyword ) ;
}
2021-04-08 16:12:22 +02:00
}
2015-05-31 01:59:42 -03:00
}
int line = 0 ;
2017-06-09 00:23:50 -03:00
int col = 0 ;
2018-09-12 13:10:49 +02:00
int thumbnail_size = MAX ( p_size . x , p_size . y ) ;
2022-07-22 20:06:19 +02:00
Ref < Image > img = Image : : create_empty ( thumbnail_size , thumbnail_size , false , Image : : FORMAT_RGBA8 ) ;
2015-05-31 01:59:42 -03:00
2022-10-18 16:43:37 +02:00
Color bg_color = EDITOR_GET ( " text_editor/theme/highlighting/background_color " ) ;
Color keyword_color = EDITOR_GET ( " text_editor/theme/highlighting/keyword_color " ) ;
Color control_flow_keyword_color = EDITOR_GET ( " text_editor/theme/highlighting/control_flow_keyword_color " ) ;
Color text_color = EDITOR_GET ( " text_editor/theme/highlighting/text_color " ) ;
Color symbol_color = EDITOR_GET ( " text_editor/theme/highlighting/symbol_color " ) ;
Color comment_color = EDITOR_GET ( " text_editor/theme/highlighting/comment_color " ) ;
2023-02-05 12:01:01 +03:00
Color doc_comment_color = EDITOR_GET ( " text_editor/theme/highlighting/doc_comment_color " ) ;
2015-05-31 01:59:42 -03:00
2020-05-14 16:41:43 +02:00
if ( bg_color . a = = 0 ) {
2017-12-30 13:38:51 +01:00
bg_color = Color ( 0 , 0 , 0 , 0 ) ;
2020-05-14 16:41:43 +02:00
}
2024-01-26 13:03:32 +01:00
bg_color . a = MAX ( bg_color . a , 0.2 ) ; // Ensure we have some background, regardless of the text editor setting.
2017-12-30 13:38:51 +01:00
2022-01-08 17:43:15 +05:45
img - > fill ( bg_color ) ;
2015-05-31 01:59:42 -03:00
2017-12-30 13:38:51 +01:00
const int x0 = thumbnail_size / 8 ;
const int y0 = thumbnail_size / 8 ;
const int available_height = thumbnail_size - 2 * y0 ;
col = x0 ;
2017-06-09 00:23:50 -03:00
bool prev_is_text = false ;
2021-04-08 16:12:22 +02:00
bool in_control_flow_keyword = false ;
2017-06-09 00:23:50 -03:00
bool in_keyword = false ;
2021-05-02 21:48:37 -03:00
bool in_comment = false ;
2023-02-05 12:01:01 +03:00
bool in_doc_comment = false ;
2024-01-26 13:03:32 +01:00
for ( int i = 0 ; i < p_source_code . length ( ) ; i + + ) {
char32_t c = p_source_code [ i ] ;
2017-06-09 00:23:50 -03:00
if ( c > 32 ) {
if ( col < thumbnail_size ) {
2015-05-31 01:59:42 -03:00
Color color = text_color ;
2021-05-02 21:48:37 -03:00
if ( c = = ' # ' ) {
2024-01-26 13:03:32 +01:00
if ( i < p_source_code . length ( ) - 1 & & p_source_code [ i + 1 ] = = ' # ' ) {
2023-02-05 12:01:01 +03:00
in_doc_comment = true ;
} else {
in_comment = true ;
}
2021-05-02 21:48:37 -03:00
}
2015-05-31 01:59:42 -03:00
2021-05-02 21:48:37 -03:00
if ( in_comment ) {
color = comment_color ;
2023-02-05 12:01:01 +03:00
} else if ( in_doc_comment ) {
color = doc_comment_color ;
2021-05-02 21:48:37 -03:00
} else {
2022-02-04 10:32:20 +02:00
if ( is_symbol ( c ) ) {
2024-01-26 13:03:32 +01:00
// Make symbol a little visible.
2021-05-02 21:48:37 -03:00
color = symbol_color ;
2021-04-08 16:12:22 +02:00
in_control_flow_keyword = false ;
2021-05-02 21:48:37 -03:00
in_keyword = false ;
2022-02-04 10:32:20 +02:00
} else if ( ! prev_is_text & & is_ascii_identifier_char ( c ) ) {
2021-05-02 21:48:37 -03:00
int pos = i ;
2024-01-26 13:03:32 +01:00
while ( is_ascii_identifier_char ( p_source_code [ pos ] ) ) {
2021-05-02 21:48:37 -03:00
pos + + ;
}
2024-01-26 13:03:32 +01:00
String word = p_source_code . substr ( i , pos - i ) ;
2021-04-08 16:12:22 +02:00
if ( control_flow_keywords . has ( word ) ) {
in_control_flow_keyword = true ;
} else if ( keywords . has ( word ) ) {
2021-05-02 21:48:37 -03:00
in_keyword = true ;
}
2022-02-04 10:32:20 +02:00
} else if ( ! is_ascii_identifier_char ( c ) ) {
2021-05-02 21:48:37 -03:00
in_keyword = false ;
2020-05-14 16:41:43 +02:00
}
2015-05-31 01:59:42 -03:00
2021-04-08 16:12:22 +02:00
if ( in_control_flow_keyword ) {
color = control_flow_keyword_color ;
} else if ( in_keyword ) {
2021-05-02 21:48:37 -03:00
color = keyword_color ;
}
2020-05-14 16:41:43 +02:00
}
2017-06-09 00:23:50 -03:00
Color ul = color ;
ul . a * = 0.5 ;
2017-12-30 13:38:51 +01:00
img - > set_pixel ( col , y0 + line * 2 , bg_color . blend ( ul ) ) ;
img - > set_pixel ( col , y0 + line * 2 + 1 , color ) ;
2015-05-31 01:59:42 -03:00
2022-02-04 10:32:20 +02:00
prev_is_text = is_ascii_identifier_char ( c ) ;
2015-05-31 01:59:42 -03:00
}
2021-05-02 21:48:37 -03:00
col + + ;
2015-05-31 01:59:42 -03:00
} else {
2017-06-09 00:23:50 -03:00
prev_is_text = false ;
2021-04-08 16:12:22 +02:00
in_control_flow_keyword = false ;
2017-06-09 00:23:50 -03:00
in_keyword = false ;
2015-05-31 01:59:42 -03:00
2017-06-09 00:23:50 -03:00
if ( c = = ' \n ' ) {
2021-05-02 21:48:37 -03:00
in_comment = false ;
2023-02-05 12:01:01 +03:00
in_doc_comment = false ;
2021-05-02 21:48:37 -03:00
2017-12-30 13:38:51 +01:00
col = x0 ;
2015-05-31 01:59:42 -03:00
line + + ;
2020-05-14 16:41:43 +02:00
if ( line > = available_height / 2 ) {
2015-05-31 01:59:42 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-06-09 00:23:50 -03:00
} else if ( c = = ' \t ' ) {
col + = 3 ;
2021-05-02 21:48:37 -03:00
} else {
col + + ;
2015-05-31 01:59:42 -03:00
}
}
}
2017-12-30 13:38:51 +01:00
post_process_preview ( img ) ;
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2015-05-31 01:59:42 -03:00
}
///////////////////////////////////////////////////////////////////
2017-08-26 17:46:49 +02:00
2018-06-07 12:46:14 -03:00
bool EditorAudioStreamPreviewPlugin : : handles ( const String & p_type ) const {
return ClassDB : : is_parent_class ( p_type , " AudioStream " ) ;
2015-05-31 01:59:42 -03:00
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorAudioStreamPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2018-06-07 12:46:14 -03:00
Ref < AudioStream > stream = p_from ;
2019-06-11 15:43:37 -03:00
ERR_FAIL_COND_V ( stream . is_null ( ) , Ref < Texture2D > ( ) ) ;
2015-05-31 01:59:42 -03:00
2020-02-17 18:06:54 -03:00
Vector < uint8_t > img ;
2018-09-12 13:10:49 +02:00
int w = p_size . x ;
int h = p_size . y ;
2018-06-07 12:46:14 -03:00
img . resize ( w * h * 3 ) ;
2015-05-31 01:59:42 -03:00
2020-02-17 18:06:54 -03:00
uint8_t * imgdata = img . ptrw ( ) ;
uint8_t * imgw = imgdata ;
2015-05-31 01:59:42 -03:00
2022-07-21 01:00:58 +02:00
Ref < AudioStreamPlayback > playback = stream - > instantiate_playback ( ) ;
2019-06-11 15:43:37 -03:00
ERR_FAIL_COND_V ( playback . is_null ( ) , Ref < Texture2D > ( ) ) ;
2015-05-31 01:59:42 -03:00
2021-08-09 17:15:17 -05:00
real_t len_s = stream - > get_length ( ) ;
2018-06-07 12:46:14 -03:00
if ( len_s = = 0 ) {
len_s = 60 ; //one minute audio if no length specified
}
int frame_length = AudioServer : : get_singleton ( ) - > get_mix_rate ( ) * len_s ;
2015-05-31 01:59:42 -03:00
2018-06-07 12:46:14 -03:00
Vector < AudioFrame > frames ;
frames . resize ( frame_length ) ;
2015-05-31 01:59:42 -03:00
2018-06-07 12:46:14 -03:00
playback - > start ( ) ;
playback - > mix ( frames . ptrw ( ) , 1 , frames . size ( ) ) ;
playback - > stop ( ) ;
2015-05-31 01:59:42 -03:00
2018-06-07 12:46:14 -03:00
for ( int i = 0 ; i < w ; i + + ) {
2021-08-09 17:15:17 -05:00
real_t max = - 1000 ;
real_t min = 1000 ;
2018-06-07 12:46:14 -03:00
int from = uint64_t ( i ) * frame_length / w ;
2019-06-03 21:52:50 +02:00
int to = ( uint64_t ( i ) + 1 ) * frame_length / w ;
2018-06-07 12:46:14 -03:00
to = MIN ( to , frame_length ) ;
from = MIN ( from , frame_length - 1 ) ;
if ( to = = from ) {
to = from + 1 ;
}
2015-05-31 01:59:42 -03:00
2018-06-07 12:46:14 -03:00
for ( int j = from ; j < to ; j + + ) {
2024-01-09 15:01:12 +01:00
max = MAX ( max , frames [ j ] . left ) ;
max = MAX ( max , frames [ j ] . right ) ;
2015-05-31 01:59:42 -03:00
2024-01-09 15:01:12 +01:00
min = MIN ( min , frames [ j ] . left ) ;
min = MIN ( min , frames [ j ] . right ) ;
2018-06-07 12:46:14 -03:00
}
2015-05-31 01:59:42 -03:00
2018-06-07 12:46:14 -03:00
int pfrom = CLAMP ( ( min * 0.5 + 0.5 ) * h / 2 , 0 , h / 2 ) + h / 4 ;
int pto = CLAMP ( ( max * 0.5 + 0.5 ) * h / 2 , 0 , h / 2 ) + h / 4 ;
2015-05-31 01:59:42 -03:00
2018-06-07 12:46:14 -03:00
for ( int j = 0 ; j < h ; j + + ) {
uint8_t * p = & imgw [ ( j * w + i ) * 3 ] ;
if ( j < pfrom | | j > pto ) {
p [ 0 ] = 100 ;
p [ 1 ] = 100 ;
p [ 2 ] = 100 ;
2015-05-31 01:59:42 -03:00
} else {
2018-06-07 12:46:14 -03:00
p [ 0 ] = 180 ;
p [ 1 ] = 180 ;
p [ 2 ] = 180 ;
2015-05-31 01:59:42 -03:00
}
}
}
2023-05-12 22:39:05 +02:00
p_metadata [ " length " ] = stream - > get_length ( ) ;
2018-06-07 12:46:14 -03:00
//post_process_preview(img);
2015-05-31 01:59:42 -03:00
2022-07-22 20:06:19 +02:00
Ref < Image > image = Image : : create_from_data ( w , h , false , Image : : FORMAT_RGB8 , img ) ;
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( image ) ;
2015-05-31 01:59:42 -03:00
}
///////////////////////////////////////////////////////////////////////////
2015-05-31 21:13:24 -03:00
2023-11-10 15:27:51 +01:00
void EditorMeshPreviewPlugin : : abort ( ) {
2024-02-21 10:44:21 +01:00
draw_requester . abort ( ) ;
2023-11-10 15:27:51 +01:00
}
2017-06-09 00:23:50 -03:00
bool EditorMeshPreviewPlugin : : handles ( const String & p_type ) const {
2022-02-06 14:12:19 +01:00
return ClassDB : : is_parent_class ( p_type , " Mesh " ) ; // Any mesh.
2017-06-09 00:23:50 -03:00
}
2015-05-31 21:13:24 -03:00
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorMeshPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2015-05-31 21:13:24 -03:00
Ref < Mesh > mesh = p_from ;
2019-06-11 15:43:37 -03:00
ERR_FAIL_COND_V ( mesh . is_null ( ) , Ref < Texture2D > ( ) ) ;
2015-05-31 21:13:24 -03:00
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > instance_set_base ( mesh_instance , mesh - > get_rid ( ) ) ;
2015-05-31 21:13:24 -03:00
2017-11-16 21:09:00 -05:00
AABB aabb = mesh - > get_aabb ( ) ;
2021-09-21 00:33:52 +05:45
Vector3 ofs = aabb . get_center ( ) ;
2017-06-06 20:33:51 +02:00
aabb . position - = ofs ;
2020-10-17 01:08:21 -04:00
Transform3D xform ;
2025-04-10 11:21:05 -05:00
xform . basis = Basis ( ) . rotated ( Vector3 ( 0 , 1 , 0 ) , - Math : : PI * 0.125 ) ;
xform . basis = Basis ( ) . rotated ( Vector3 ( 1 , 0 , 0 ) , Math : : PI * 0.125 ) * xform . basis ;
2017-11-16 21:09:00 -05:00
AABB rot_aabb = xform . xform ( aabb ) ;
2021-06-20 23:30:19 -04:00
real_t m = MAX ( rot_aabb . size . x , rot_aabb . size . y ) * 0.5 ;
2020-05-14 16:41:43 +02:00
if ( m = = 0 ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-06-09 00:23:50 -03:00
m = 1.0 / m ;
m * = 0.5 ;
xform . basis . scale ( Vector3 ( m , m , m ) ) ;
xform . origin = - xform . basis . xform ( ofs ) ; //-ofs*m;
xform . origin . z - = rot_aabb . size . z * 2 ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > instance_set_transform ( mesh_instance , xform ) ;
2015-05-31 21:13:24 -03:00
2024-02-21 10:44:21 +01:00
draw_requester . request_and_wait ( viewport ) ;
2025-05-31 16:57:51 +08:00
if ( EditorResourcePreview : : get_singleton ( ) - > is_threaded ( ) ) {
draw_requester . request_and_wait ( viewport ) ; // Wait twice when run on thread, or else won't render correctly. comment this line to see the bug
}
2015-05-31 21:13:24 -03:00
2020-03-27 15:21:27 -03:00
Ref < Image > img = RS : : get_singleton ( ) - > texture_2d_get ( viewport_texture ) ;
2017-06-09 00:23:50 -03:00
ERR_FAIL_COND_V ( img . is_null ( ) , Ref < ImageTexture > ( ) ) ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > instance_set_base ( mesh_instance , RID ( ) ) ;
2015-05-31 21:13:24 -03:00
2017-06-09 00:23:50 -03:00
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2018-09-12 13:10:49 +02:00
Vector2 new_size = img - > get_size ( ) ;
if ( new_size . x > p_size . x ) {
new_size = Vector2 ( p_size . x , new_size . y * p_size . x / new_size . x ) ;
}
if ( new_size . y > p_size . y ) {
new_size = Vector2 ( new_size . x * p_size . y / new_size . y , p_size . y ) ;
}
img - > resize ( new_size . x , new_size . y , Image : : INTERPOLATE_CUBIC ) ;
2017-12-30 13:38:51 +01:00
post_process_preview ( img ) ;
2015-05-31 21:13:24 -03:00
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2015-05-31 21:13:24 -03:00
}
EditorMeshPreviewPlugin : : EditorMeshPreviewPlugin ( ) {
2020-03-27 15:21:27 -03:00
scenario = RS : : get_singleton ( ) - > scenario_create ( ) ;
2017-06-09 00:23:50 -03:00
2020-03-27 15:21:27 -03:00
viewport = RS : : get_singleton ( ) - > viewport_create ( ) ;
RS : : get_singleton ( ) - > viewport_set_update_mode ( viewport , RS : : VIEWPORT_UPDATE_DISABLED ) ;
RS : : get_singleton ( ) - > viewport_set_scenario ( viewport , scenario ) ;
RS : : get_singleton ( ) - > viewport_set_size ( viewport , 128 , 128 ) ;
RS : : get_singleton ( ) - > viewport_set_transparent_background ( viewport , true ) ;
RS : : get_singleton ( ) - > viewport_set_active ( viewport , true ) ;
viewport_texture = RS : : get_singleton ( ) - > viewport_get_texture ( viewport ) ;
2015-05-31 21:13:24 -03:00
2020-03-27 15:21:27 -03:00
camera = RS : : get_singleton ( ) - > camera_create ( ) ;
RS : : get_singleton ( ) - > viewport_attach_camera ( viewport , camera ) ;
2020-10-17 01:08:21 -04:00
RS : : get_singleton ( ) - > camera_set_transform ( camera , Transform3D ( Basis ( ) , Vector3 ( 0 , 0 , 3 ) ) ) ;
2020-03-27 15:21:27 -03:00
//RS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
RS : : get_singleton ( ) - > camera_set_orthogonal ( camera , 1.0 , 0.01 , 1000.0 ) ;
2015-05-31 21:13:24 -03:00
2022-07-31 16:20:24 -07:00
if ( GLOBAL_GET ( " rendering/lights_and_shadows/use_physical_light_units " ) ) {
camera_attributes = RS : : get_singleton ( ) - > camera_attributes_create ( ) ;
RS : : get_singleton ( ) - > camera_attributes_set_exposure ( camera_attributes , 1.0 , 0.000032552 ) ; // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
RS : : get_singleton ( ) - > camera_set_camera_attributes ( camera , camera_attributes ) ;
}
2020-03-27 15:21:27 -03:00
light = RS : : get_singleton ( ) - > directional_light_create ( ) ;
light_instance = RS : : get_singleton ( ) - > instance_create2 ( light , scenario ) ;
2020-10-17 01:08:21 -04:00
RS : : get_singleton ( ) - > instance_set_transform ( light_instance , Transform3D ( ) . looking_at ( Vector3 ( - 1 , - 1 , - 1 ) , Vector3 ( 0 , 1 , 0 ) ) ) ;
2015-05-31 21:13:24 -03:00
2020-03-27 15:21:27 -03:00
light2 = RS : : get_singleton ( ) - > directional_light_create ( ) ;
RS : : get_singleton ( ) - > light_set_color ( light2 , Color ( 0.7 , 0.7 , 0.7 ) ) ;
//RS::get_singleton()->light_set_color(light2, RS::LIGHT_COLOR_SPECULAR, Color(0.0, 0.0, 0.0));
light_instance2 = RS : : get_singleton ( ) - > instance_create2 ( light2 , scenario ) ;
2015-05-31 21:13:24 -03:00
2020-10-17 01:08:21 -04:00
RS : : get_singleton ( ) - > instance_set_transform ( light_instance2 , Transform3D ( ) . looking_at ( Vector3 ( 0 , 1 , 0 ) , Vector3 ( 0 , 0 , 1 ) ) ) ;
2015-05-31 21:13:24 -03:00
2020-03-27 15:21:27 -03:00
//sphere = RS::get_singleton()->mesh_create();
mesh_instance = RS : : get_singleton ( ) - > instance_create ( ) ;
RS : : get_singleton ( ) - > instance_set_scenario ( mesh_instance , scenario ) ;
2015-05-31 21:13:24 -03:00
}
EditorMeshPreviewPlugin : : ~ EditorMeshPreviewPlugin ( ) {
2022-12-12 12:42:37 -05:00
ERR_FAIL_NULL ( RenderingServer : : get_singleton ( ) ) ;
2020-03-27 15:21:27 -03:00
//RS::get_singleton()->free(sphere);
RS : : get_singleton ( ) - > free ( mesh_instance ) ;
RS : : get_singleton ( ) - > free ( viewport ) ;
RS : : get_singleton ( ) - > free ( light ) ;
RS : : get_singleton ( ) - > free ( light_instance ) ;
RS : : get_singleton ( ) - > free ( light2 ) ;
RS : : get_singleton ( ) - > free ( light_instance2 ) ;
RS : : get_singleton ( ) - > free ( camera ) ;
2022-07-31 16:20:24 -07:00
RS : : get_singleton ( ) - > free ( camera_attributes ) ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > free ( scenario ) ;
2015-05-31 21:13:24 -03:00
}
2018-04-15 22:00:56 -05:00
///////////////////////////////////////////////////////////////////////////
2023-11-10 15:27:51 +01:00
void EditorFontPreviewPlugin : : abort ( ) {
2024-02-21 10:44:21 +01:00
draw_requester . abort ( ) ;
2023-11-10 15:27:51 +01:00
}
2018-04-15 22:00:56 -05:00
bool EditorFontPreviewPlugin : : handles ( const String & p_type ) const {
2022-05-09 12:47:10 +03:00
return ClassDB : : is_parent_class ( p_type , " Font " ) ;
2020-09-03 14:22:16 +03:00
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorFontPreviewPlugin : : generate_from_path ( const String & p_path , const Size2 & p_size , Dictionary & p_metadata ) const {
2022-05-09 12:47:10 +03:00
Ref < Font > sampled_font = ResourceLoader : : load ( p_path ) ;
ERR_FAIL_COND_V ( sampled_font . is_null ( ) , Ref < Texture2D > ( ) ) ;
2018-04-15 22:00:56 -05:00
2020-09-03 14:22:16 +03:00
String sample ;
2020-12-27 15:30:33 +02:00
static const String sample_base = U " 12漢字ԱբΑ α А б Α α אבا بܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀ ᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀 " ;
for ( int i = 0 ; i < sample_base . length ( ) ; i + + ) {
if ( sampled_font - > has_char ( sample_base [ i ] ) ) {
sample + = sample_base [ i ] ;
2020-09-03 14:22:16 +03:00
}
}
2020-12-27 15:30:33 +02:00
if ( sample . is_empty ( ) ) {
sample = sampled_font - > get_supported_chars ( ) . substr ( 0 , 6 ) ;
}
2022-05-09 12:47:10 +03:00
Vector2 size = sampled_font - > get_string_size ( sample , HORIZONTAL_ALIGNMENT_LEFT , - 1 , 50 ) ;
2018-04-15 22:00:56 -05:00
Vector2 pos ;
pos . x = 64 - size . x / 2 ;
pos . y = 80 ;
2021-12-16 13:59:04 +08:00
const Color c = GLOBAL_GET ( " rendering/environment/defaults/default_clear_color " ) ;
const float fg = c . get_luminance ( ) < 0.5 ? 1.0 : 0.0 ;
2022-05-09 12:47:10 +03:00
sampled_font - > draw_string ( canvas_item , pos , sample , HORIZONTAL_ALIGNMENT_LEFT , - 1.f , 50 , Color ( fg , fg , fg ) ) ;
2018-04-15 22:00:56 -05:00
2024-02-21 10:44:21 +01:00
draw_requester . request_and_wait ( viewport ) ;
2018-04-15 22:00:56 -05:00
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > canvas_item_clear ( canvas_item ) ;
2019-02-23 21:30:39 -03:00
2020-03-27 15:21:27 -03:00
Ref < Image > img = RS : : get_singleton ( ) - > texture_2d_get ( viewport_texture ) ;
2018-04-15 22:00:56 -05:00
ERR_FAIL_COND_V ( img . is_null ( ) , Ref < ImageTexture > ( ) ) ;
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2018-09-12 13:10:49 +02:00
Vector2 new_size = img - > get_size ( ) ;
if ( new_size . x > p_size . x ) {
new_size = Vector2 ( p_size . x , new_size . y * p_size . x / new_size . x ) ;
}
if ( new_size . y > p_size . y ) {
new_size = Vector2 ( new_size . x * p_size . y / new_size . y , p_size . y ) ;
}
img - > resize ( new_size . x , new_size . y , Image : : INTERPOLATE_CUBIC ) ;
2018-04-15 22:00:56 -05:00
post_process_preview ( img ) ;
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( img ) ;
2018-04-15 22:00:56 -05:00
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorFontPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2019-02-23 21:30:39 -03:00
String path = p_from - > get_path ( ) ;
if ( ! FileAccess : : exists ( path ) ) {
2019-06-11 15:43:37 -03:00
return Ref < Texture2D > ( ) ;
2019-02-23 21:30:39 -03:00
}
2022-08-19 18:14:57 +02:00
return generate_from_path ( path , p_size , p_metadata ) ;
2018-04-15 22:00:56 -05:00
}
EditorFontPreviewPlugin : : EditorFontPreviewPlugin ( ) {
2020-03-27 15:21:27 -03:00
viewport = RS : : get_singleton ( ) - > viewport_create ( ) ;
RS : : get_singleton ( ) - > viewport_set_update_mode ( viewport , RS : : VIEWPORT_UPDATE_DISABLED ) ;
RS : : get_singleton ( ) - > viewport_set_size ( viewport , 128 , 128 ) ;
RS : : get_singleton ( ) - > viewport_set_active ( viewport , true ) ;
viewport_texture = RS : : get_singleton ( ) - > viewport_get_texture ( viewport ) ;
2018-07-29 16:45:23 -03:00
2020-03-27 15:21:27 -03:00
canvas = RS : : get_singleton ( ) - > canvas_create ( ) ;
canvas_item = RS : : get_singleton ( ) - > canvas_item_create ( ) ;
2018-07-29 16:45:23 -03:00
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > viewport_attach_canvas ( viewport , canvas ) ;
RS : : get_singleton ( ) - > canvas_item_set_parent ( canvas_item , canvas ) ;
2018-04-15 22:00:56 -05:00
}
EditorFontPreviewPlugin : : ~ EditorFontPreviewPlugin ( ) {
2022-12-12 12:42:37 -05:00
ERR_FAIL_NULL ( RenderingServer : : get_singleton ( ) ) ;
2020-03-27 15:21:27 -03:00
RS : : get_singleton ( ) - > free ( canvas_item ) ;
RS : : get_singleton ( ) - > free ( canvas ) ;
RS : : get_singleton ( ) - > free ( viewport ) ;
2018-04-15 22:00:56 -05:00
}
2022-04-21 00:08:35 +02:00
////////////////////////////////////////////////////////////////////////////
static const real_t GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR = 4.0 ;
bool EditorGradientPreviewPlugin : : handles ( const String & p_type ) const {
return ClassDB : : is_parent_class ( p_type , " Gradient " ) ;
}
bool EditorGradientPreviewPlugin : : generate_small_preview_automatically ( ) const {
return true ;
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorGradientPreviewPlugin : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2022-04-21 00:08:35 +02:00
Ref < Gradient > gradient = p_from ;
if ( gradient . is_valid ( ) ) {
Ref < GradientTexture1D > ptex ;
ptex . instantiate ( ) ;
ptex - > set_width ( p_size . width * GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR * EDSCALE ) ;
ptex - > set_gradient ( gradient ) ;
2022-05-04 01:49:20 +02:00
return ImageTexture : : create_from_image ( ptex - > get_image ( ) ) ;
2022-04-21 00:08:35 +02:00
}
return Ref < Texture2D > ( ) ;
}