5656#include " main/main.h"
5757#include " scene/3d/light_3d.h"
5858#include " scene/3d/mesh_instance_3d.h"
59+ #include " scene/3d/world_environment.h"
5960#include " scene/gui/box_container.h"
6061#include " scene/gui/control.h"
6162#include " scene/main/window.h"
@@ -108,15 +109,8 @@ EditorUndoRedoManager *EditorInterface::get_editor_undo_redo() const {
108109AABB EditorInterface::_calculate_aabb_for_scene (Node *p_node, AABB &p_scene_aabb) {
109110 MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node);
110111 if (mesh_node && mesh_node->get_mesh ().is_valid ()) {
111- Transform3D accum_xform;
112- Node3D *base = mesh_node;
113- while (base) {
114- accum_xform = base->get_transform () * accum_xform;
115- base = Object::cast_to<Node3D>(base->get_parent ());
116- }
117-
118- AABB aabb = accum_xform.xform (mesh_node->get_mesh ()->get_aabb ());
119- p_scene_aabb.merge_with (aabb);
112+ AABB mesh_aabb = mesh_node->get_transform ().xform (mesh_node->get_aabb ()); // Should not be necessary, as imported transform already be reset, but just in case.
113+ p_scene_aabb.merge_with (mesh_aabb);
120114 }
121115
122116 for (int i = 0 ; i < p_node->get_child_count (); i++) {
@@ -236,75 +230,79 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
236230 return ;
237231 }
238232 ERR_FAIL_COND_MSG (p_path.is_empty (), " Path is empty, cannot generate preview." );
239- ERR_FAIL_NULL_MSG (p_scene, " The provided scene is null, cannot generate preview ." );
233+ ERR_FAIL_NULL_MSG (p_scene, " The provided scene is null." );
240234 ERR_FAIL_COND_MSG (p_scene->is_inside_tree (), " The scene must not be inside the tree." );
241235 ERR_FAIL_NULL_MSG (EditorNode::get_singleton (), " EditorNode doesn't exist." );
236+ ERR_FAIL_COND_MSG (!Engine::get_singleton ()->is_editor_hint (), " This function can only be called from the editor." );
242237
243238 SubViewport *sub_viewport_node = memnew (SubViewport);
239+ EditorNode::get_singleton ()->add_child (sub_viewport_node);
244240 AABB scene_aabb;
245241 scene_aabb = _calculate_aabb_for_scene (p_scene, scene_aabb);
246242
247- sub_viewport_node->set_update_mode (SubViewport::UPDATE_ALWAYS );
243+ sub_viewport_node->set_update_mode (SubViewport::UPDATE_ONCE );
248244 sub_viewport_node->set_size (Vector2i (p_preview_size, p_preview_size));
249245 sub_viewport_node->set_transparent_background (false );
246+
247+ // Doing this so it won't popup in editor viewport
250248 Ref<World3D> world;
251249 world.instantiate ();
252250 sub_viewport_node->set_world_3d (world);
253251
254- EditorNode::get_singleton ()->add_child (sub_viewport_node);
252+ // Supersampling x2
253+ if (p_preview_size < 2048 ) { // Universal baseline for textures in Godot 4 is 4K
254+ sub_viewport_node->set_scaling_3d_scale (2.0 );
255+ }
256+
257+ // MSAA if using forward renderer
258+ if (RS::get_singleton ()->get_current_rendering_method () != " gl_compatibility" ) {
259+ sub_viewport_node->set_msaa_3d (Viewport::MSAA::MSAA_8X);
260+ }
261+
255262 Ref<Environment> env;
256263 env.instantiate ();
264+ Color default_clear_color = GLOBAL_GET (" rendering/environment/defaults/default_clear_color" );
257265 env->set_background (Environment::BG_CLEAR_COLOR);
258-
259- Ref<CameraAttributesPractical> camera_attributes;
260- camera_attributes.instantiate ();
266+ env->set_bg_color (default_clear_color);
261267
262268 Node3D *root = memnew (Node3D);
263- root->set_name (" Root" );
264269 sub_viewport_node->add_child (root);
270+ root->add_child (p_scene);
265271
266272 Camera3D *camera = memnew (Camera3D);
267- camera->set_environment (env);
268- camera->set_attributes (camera_attributes);
269- camera->set_name (" Camera3D" );
270273 root->add_child (camera);
274+ camera->set_environment (env);
271275 camera->set_current (true );
272276
273- camera->set_position (Vector3 (0.0 , 0.0 , 3.0 ));
274-
275- DirectionalLight3D *light = memnew (DirectionalLight3D);
276- light->set_name (" Light" );
277- DirectionalLight3D *light2 = memnew (DirectionalLight3D);
278- light2->set_name (" Light2" );
279- light2->set_color (Color (0.7 , 0.7 , 0.7 , 1.0 ));
280-
281- root->add_child (light);
282- root->add_child (light2);
283-
284- sub_viewport_node->add_child (p_scene);
277+ DirectionalLight3D *light_1 = memnew (DirectionalLight3D);
278+ DirectionalLight3D *light_2 = memnew (DirectionalLight3D);
279+ root->add_child (light_1);
280+ root->add_child (light_2);
285281
286- // Calculate the camera and lighting position based on the size of the scene.
287- Vector3 center = scene_aabb.get_center ();
288- float camera_size = scene_aabb.get_longest_axis_size ();
289-
290- const float cam_rot_x = -Math::PI / 4 ;
291- const float cam_rot_y = -Math::PI / 4 ;
292-
293- camera->set_orthogonal (camera_size * 2.0 , 0.0001 , camera_size * 2.0 );
282+ // Setup preview camera
283+ float bound_sphere_radius = (scene_aabb.get_end () - scene_aabb.get_position ()).length () / 2.0 ;
284+ if (bound_sphere_radius <= 0.0 ) {
285+ // The scene has zero volume, so just it give a literal
286+ bound_sphere_radius = 1.0 ;
287+ }
294288
295- Transform3D xf ;
296- xf. basis = Basis ( Vector3 ( 0 , 1 , 0 ), cam_rot_y) * Basis ( Vector3 ( 1 , 0 , 0 ), cam_rot_x );
297- xf. origin = center ;
298- xf. translate_local ( 0 , 0 , camera_size) ;
289+ const float cam_fov = 30.0 ;
290+ const float cam_distance = bound_sphere_radius / Math::tan ( Math::deg_to_rad (cam_fov) / 2.0 );
291+ const float cam_near = cam_distance * 0.01 ;
292+ const float cam_far = cam_distance * 2.0 ;
299293
300- camera->set_transform (xf );
294+ camera->set_perspective (cam_fov, cam_near, cam_far );
301295
302- Transform3D xform;
303- xform.basis = Basis ().rotated (Vector3 (0 , 1 , 0 ), -Math::PI / 6 );
304- xform.basis = Basis ().rotated (Vector3 (1 , 0 , 0 ), Math::PI / 6 ) * xform.basis ;
296+ Transform3D cam_t3d;
297+ cam_t3d.set_origin (scene_aabb.get_center () + Vector3 (1 .0f , 0 .25f , 1 .0f ).normalized () * cam_distance);
298+ cam_t3d.set_look_at (cam_t3d.origin , scene_aabb.get_center ());
299+ camera->set_transform (cam_t3d);
305300
306- light->set_transform (xform * Transform3D ().looking_at (Vector3 (-2 , -1 , -1 ), Vector3 (0 , 1 , 0 )));
307- light2->set_transform (xform * Transform3D ().looking_at (Vector3 (+1 , -1 , -2 ), Vector3 (0 , 1 , 0 )));
301+ // Setup preview lighting
302+ light_1->set_color (Color (1.0 , 1.0 , 1.0 , 1.0 ));
303+ light_2->set_color (Color (0.7 , 0.7 , 0.7 , 1.0 ));
304+ light_1->set_transform (Transform3D (Basis ().rotated (Vector3 (0 , 1 , 0 ), -Math::PI / 6 ), Vector3 (0.0 , 0.0 , 0.0 )));
305+ light_2->set_transform (Transform3D (Basis ().rotated (Vector3 (1 , 0 , 0 ), -Math::PI / 6 ), Vector3 (0.0 , 0.0 , 0.0 )));
308306
309307 // Update the renderer to get the screenshot.
310308 DisplayServer::get_singleton ()->process_events ();
@@ -313,45 +311,18 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
313311
314312 // Get the texture.
315313 Ref<Texture2D> texture = sub_viewport_node->get_texture ();
316- ERR_FAIL_COND_MSG (texture.is_null (), " Failed to get texture from sub_viewport_node." );
317-
318- // Remove the initial scene node.
319- sub_viewport_node->remove_child (p_scene);
320314
321- // Cleanup the viewport.
322- if (sub_viewport_node) {
323- if (sub_viewport_node->get_parent ()) {
324- sub_viewport_node->get_parent ()->remove_child (sub_viewport_node);
325- }
315+ if (texture.is_null ()) {
316+ ERR_PRINT (vformat (R"( Failed to create preview for "%s", preview SubViewport texture is invalid.)" , p_path));
326317 sub_viewport_node->queue_free ();
327- sub_viewport_node = nullptr ;
318+ return ;
328319 }
329320
330- // Now generate the cache image.
321+ // Retrieve and save preview image
331322 Ref<Image> img = texture->get_image ();
332323 if (img.is_valid () && img->get_width () > 0 && img->get_height () > 0 ) {
333324 img = img->duplicate ();
334-
335- int preview_size = EDITOR_GET (" filesystem/file_dialog/thumbnail_size" );
336- preview_size *= EDSCALE;
337-
338- int vp_size = MIN (img->get_width (), img->get_height ());
339- int x = (img->get_width () - vp_size) / 2 ;
340- int y = (img->get_height () - vp_size) / 2 ;
341-
342- if (vp_size < preview_size) {
343- img->crop_from_point (x, y, vp_size, vp_size);
344- } else {
345- int ratio = vp_size / preview_size;
346- int size = preview_size * MAX (1 , ratio / 2 );
347-
348- x = (img->get_width () - size) / 2 ;
349- y = (img->get_height () - size) / 2 ;
350-
351- img->crop_from_point (x, y, size, size);
352- img->resize (preview_size, preview_size, Image::INTERPOLATE_LANCZOS);
353- }
354- img->convert (Image::FORMAT_RGB8);
325+ img->convert (Image::FORMAT_RGBA8);
355326
356327 String temp_path = EditorPaths::get_singleton ()->get_cache_dir ();
357328 String cache_base = ProjectSettings::get_singleton ()->globalize_path (p_path).md5_text ();
@@ -361,6 +332,9 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
361332 img->save_png (cache_base + " .png" );
362333 }
363334
335+ // Cleanup the viewport.
336+ sub_viewport_node->queue_free ();
337+
364338 EditorResourcePreview::get_singleton ()->check_for_invalidation (p_path);
365339 EditorFileSystem::get_singleton ()->emit_signal (SNAME (" filesystem_changed" ));
366340}
0 commit comments