Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/inspector/editor_preview_plugins.cpp
20841 views
1
/**************************************************************************/
2
/* editor_preview_plugins.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "editor_preview_plugins.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/io/image.h"
35
#include "core/io/resource_loader.h"
36
#include "core/object/script_language.h"
37
#include "editor/file_system/editor_paths.h"
38
#include "editor/settings/editor_settings.h"
39
#include "editor/themes/editor_scale.h"
40
#include "scene/resources/atlas_texture.h"
41
#include "scene/resources/bit_map.h"
42
#include "scene/resources/font.h"
43
#include "scene/resources/gradient_texture.h"
44
#include "scene/resources/image_texture.h"
45
#include "scene/resources/material.h"
46
#include "scene/resources/mesh.h"
47
#include "servers/audio/audio_stream.h"
48
49
void post_process_preview(Ref<Image> p_image) {
50
if (p_image->get_format() != Image::FORMAT_RGBA8) {
51
p_image->convert(Image::FORMAT_RGBA8);
52
}
53
54
const int w = p_image->get_width();
55
const int h = p_image->get_height();
56
57
const int r = MIN(w, h) / 32;
58
const int r2 = r * r;
59
Color transparent = Color(0, 0, 0, 0);
60
61
for (int i = 0; i < r; i++) {
62
for (int j = 0; j < r; j++) {
63
int dx = i - r;
64
int dy = j - r;
65
if (dx * dx + dy * dy > r2) {
66
p_image->set_pixel(i, j, transparent);
67
p_image->set_pixel(w - 1 - i, j, transparent);
68
p_image->set_pixel(w - 1 - i, h - 1 - j, transparent);
69
p_image->set_pixel(i, h - 1 - j, transparent);
70
} else {
71
break;
72
}
73
}
74
}
75
}
76
77
bool EditorTexturePreviewPlugin::handles(const String &p_type) const {
78
return ClassDB::is_parent_class(p_type, "Texture");
79
}
80
81
bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const {
82
return true;
83
}
84
85
Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
86
Ref<Image> img;
87
88
Ref<AtlasTexture> tex_atlas = p_from;
89
Ref<Texture3D> tex_3d = p_from;
90
Ref<TextureLayered> tex_lyr = p_from;
91
92
if (tex_atlas.is_valid()) {
93
Ref<Texture2D> tex = tex_atlas->get_atlas();
94
if (tex.is_null()) {
95
return Ref<Texture2D>();
96
}
97
98
Ref<Image> atlas = tex->get_image();
99
if (atlas.is_null()) {
100
return Ref<Texture2D>();
101
}
102
103
if (atlas->is_compressed()) {
104
atlas = atlas->duplicate();
105
if (atlas->decompress() != OK) {
106
return Ref<Texture2D>();
107
}
108
}
109
110
if (!tex_atlas->get_region().has_area()) {
111
return Ref<Texture2D>();
112
}
113
114
img = atlas->get_region(tex_atlas->get_region());
115
116
} else if (tex_3d.is_valid()) {
117
if (tex_3d->get_depth() == 0) {
118
return Ref<Texture2D>();
119
}
120
121
Vector<Ref<Image>> data = tex_3d->get_data();
122
if (data.size() != tex_3d->get_depth()) {
123
return Ref<Texture2D>();
124
}
125
126
// Use the middle slice for the thumbnail.
127
const int mid_depth = (tex_3d->get_depth() - 1) / 2;
128
if (!data.is_empty() && data[mid_depth].is_valid()) {
129
img = data[mid_depth]->duplicate();
130
}
131
132
} else if (tex_lyr.is_valid()) {
133
if (tex_lyr->get_layers() == 0) {
134
return Ref<Texture2D>();
135
}
136
137
// Use the middle slice for the thumbnail.
138
const int mid_layer = (tex_lyr->get_layers() - 1) / 2;
139
140
Ref<Image> data = tex_lyr->get_layer_data(mid_layer);
141
if (data.is_valid()) {
142
img = data->duplicate();
143
}
144
145
} else {
146
Ref<Texture2D> tex = p_from;
147
if (tex.is_valid()) {
148
img = tex->get_image();
149
if (img.is_valid()) {
150
img = img->duplicate();
151
}
152
}
153
}
154
155
if (img.is_null() || img->is_empty()) {
156
return Ref<Texture2D>();
157
}
158
159
p_metadata["dimensions"] = img->get_size();
160
161
img->clear_mipmaps();
162
163
if (img->is_compressed()) {
164
if (img->decompress() != OK) {
165
return Ref<Texture2D>();
166
}
167
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
168
img->convert(Image::FORMAT_RGBA8);
169
}
170
171
Vector2 new_size = img->get_size();
172
if (new_size.x > p_size.x) {
173
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
174
}
175
if (new_size.y > p_size.y) {
176
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
177
}
178
Vector2i new_size_i = Vector2i(new_size).maxi(1);
179
img->resize(new_size_i.x, new_size_i.y, Image::INTERPOLATE_CUBIC);
180
post_process_preview(img);
181
182
return ImageTexture::create_from_image(img);
183
}
184
185
////////////////////////////////////////////////////////////////////////////
186
187
bool EditorImagePreviewPlugin::handles(const String &p_type) const {
188
return p_type == "Image";
189
}
190
191
Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
192
Ref<Image> img = p_from;
193
194
if (img.is_null() || img->is_empty()) {
195
return Ref<Texture2D>();
196
}
197
198
img = img->duplicate();
199
img->clear_mipmaps();
200
201
if (img->is_compressed()) {
202
if (img->decompress() != OK) {
203
return Ref<Texture2D>();
204
}
205
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
206
img->convert(Image::FORMAT_RGBA8);
207
}
208
209
Vector2 new_size = img->get_size();
210
if (new_size.x > p_size.x) {
211
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
212
}
213
if (new_size.y > p_size.y) {
214
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
215
}
216
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
217
post_process_preview(img);
218
219
return ImageTexture::create_from_image(img);
220
}
221
222
bool EditorImagePreviewPlugin::generate_small_preview_automatically() const {
223
return true;
224
}
225
226
////////////////////////////////////////////////////////////////////////////
227
228
bool EditorBitmapPreviewPlugin::handles(const String &p_type) const {
229
return ClassDB::is_parent_class(p_type, "BitMap");
230
}
231
232
Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
233
Ref<BitMap> bm = p_from;
234
if (bm.is_null()) {
235
return Ref<Texture2D>();
236
}
237
238
if (bm->get_size() == Size2()) {
239
return Ref<Texture2D>();
240
}
241
242
Vector<uint8_t> data;
243
244
data.resize(bm->get_size().width * bm->get_size().height);
245
246
{
247
uint8_t *w = data.ptrw();
248
249
for (int i = 0; i < bm->get_size().width; i++) {
250
for (int j = 0; j < bm->get_size().height; j++) {
251
if (bm->get_bit(i, j)) {
252
w[j * (int)bm->get_size().width + i] = 255;
253
} else {
254
w[j * (int)bm->get_size().width + i] = 0;
255
}
256
}
257
}
258
}
259
260
Ref<Image> img = Image::create_from_data(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data);
261
262
if (img->is_compressed()) {
263
if (img->decompress() != OK) {
264
return Ref<Texture2D>();
265
}
266
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
267
img->convert(Image::FORMAT_RGBA8);
268
}
269
270
Vector2 new_size = img->get_size();
271
if (new_size.x > p_size.x) {
272
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
273
}
274
if (new_size.y > p_size.y) {
275
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
276
}
277
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
278
post_process_preview(img);
279
280
return ImageTexture::create_from_image(img);
281
}
282
283
bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const {
284
return true;
285
}
286
287
///////////////////////////////////////////////////////////////////////////
288
289
bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const {
290
return ClassDB::is_parent_class(p_type, "PackedScene");
291
}
292
293
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
294
return generate_from_path(p_from->get_path(), p_size, p_metadata);
295
}
296
297
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
298
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
299
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
300
cache_base = temp_path.path_join("resthumb-" + cache_base);
301
302
//does not have it, try to load a cached thumbnail
303
304
String path = cache_base + ".png";
305
306
if (!FileAccess::exists(path)) {
307
return Ref<Texture2D>();
308
}
309
310
Ref<Image> img;
311
img.instantiate();
312
Error err = img->load(path);
313
if (err == OK) {
314
post_process_preview(img);
315
return ImageTexture::create_from_image(img);
316
317
} else {
318
return Ref<Texture2D>();
319
}
320
}
321
322
//////////////////////////////////////////////////////////////////
323
324
void EditorMaterialPreviewPlugin::abort() {
325
draw_requester.abort();
326
}
327
328
bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
329
return ClassDB::is_parent_class(p_type, "Material"); // Any material.
330
}
331
332
bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
333
return true;
334
}
335
336
Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
337
Ref<Material> material = p_from;
338
ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>());
339
340
if (material->get_shader_mode() == Shader::MODE_SPATIAL) {
341
RS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
342
343
draw_requester.request_and_wait(viewport);
344
345
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
346
RS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
347
348
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
349
350
img->convert(Image::FORMAT_RGBA8);
351
int thumbnail_size = MAX(p_size.x, p_size.y);
352
img->resize(thumbnail_size, thumbnail_size, Image::INTERPOLATE_CUBIC);
353
post_process_preview(img);
354
return ImageTexture::create_from_image(img);
355
}
356
357
return Ref<Texture2D>();
358
}
359
360
EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
361
scenario = RS::get_singleton()->scenario_create();
362
363
viewport = RS::get_singleton()->viewport_create();
364
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
365
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
366
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
367
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
368
RS::get_singleton()->viewport_set_active(viewport, true);
369
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
370
371
camera = RS::get_singleton()->camera_create();
372
RS::get_singleton()->viewport_attach_camera(viewport, camera);
373
RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3)));
374
RS::get_singleton()->camera_set_perspective(camera, 45, 0.1, 10);
375
376
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
377
camera_attributes = RS::get_singleton()->camera_attributes_create();
378
RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
379
RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes);
380
}
381
382
light = RS::get_singleton()->directional_light_create();
383
light_instance = RS::get_singleton()->instance_create2(light, scenario);
384
RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
385
386
light2 = RS::get_singleton()->directional_light_create();
387
RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
388
//RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
389
390
light_instance2 = RS::get_singleton()->instance_create2(light2, scenario);
391
392
RS::get_singleton()->instance_set_transform(light_instance2, Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
393
394
sphere = RS::get_singleton()->mesh_create();
395
sphere_instance = RS::get_singleton()->instance_create2(sphere, scenario);
396
397
int lats = 32;
398
int lons = 32;
399
const double lat_step = Math::PI / lats;
400
const double lon_step = Math::TAU / lons;
401
real_t radius = 1.0;
402
403
Vector<Vector3> vertices;
404
Vector<Vector3> normals;
405
Vector<Vector2> uvs;
406
Vector<real_t> tangents;
407
Basis tt = Basis(Vector3(0, 1, 0), Math::PI * 0.5);
408
409
for (int i = 1; i <= lats; i++) {
410
double lat0 = lat_step * (i - 1) - Math::TAU / 4;
411
double z0 = Math::sin(lat0);
412
double zr0 = Math::cos(lat0);
413
414
double lat1 = lat_step * i - Math::TAU / 4;
415
double z1 = Math::sin(lat1);
416
double zr1 = Math::cos(lat1);
417
418
for (int j = lons; j >= 1; j--) {
419
double lng0 = lon_step * (j - 1);
420
double x0 = Math::cos(lng0);
421
double y0 = Math::sin(lng0);
422
423
double lng1 = lon_step * j;
424
double x1 = Math::cos(lng1);
425
double y1 = Math::sin(lng1);
426
427
Vector3 v[4] = {
428
Vector3(x1 * zr0, z0, y1 * zr0),
429
Vector3(x1 * zr1, z1, y1 * zr1),
430
Vector3(x0 * zr1, z1, y0 * zr1),
431
Vector3(x0 * zr0, z0, y0 * zr0)
432
};
433
434
#define ADD_POINT(m_idx) \
435
normals.push_back(v[m_idx]); \
436
vertices.push_back(v[m_idx] * radius); \
437
{ \
438
Vector2 uv; \
439
if (j >= lons / 2) { \
440
uv = Vector2(Math::atan2(-v[m_idx].x, -v[m_idx].z), Math::atan2(v[m_idx].y, -v[m_idx].z)); \
441
} else { \
442
uv = Vector2(Math::atan2(v[m_idx].x, v[m_idx].z), Math::atan2(-v[m_idx].y, v[m_idx].z)); \
443
} \
444
uv /= Math::PI; \
445
uv *= 4.0; \
446
uv = uv * 0.5 + Vector2(0.5, 0.5); \
447
uvs.push_back(uv); \
448
} \
449
{ \
450
Vector3 t = tt.xform(v[m_idx]); \
451
tangents.push_back(t.x); \
452
tangents.push_back(t.y); \
453
tangents.push_back(t.z); \
454
tangents.push_back(1.0); \
455
}
456
457
ADD_POINT(0);
458
ADD_POINT(1);
459
ADD_POINT(2);
460
461
ADD_POINT(2);
462
ADD_POINT(3);
463
ADD_POINT(0);
464
}
465
}
466
467
Array arr;
468
arr.resize(RS::ARRAY_MAX);
469
arr[RS::ARRAY_VERTEX] = vertices;
470
arr[RS::ARRAY_NORMAL] = normals;
471
arr[RS::ARRAY_TANGENT] = tangents;
472
arr[RS::ARRAY_TEX_UV] = uvs;
473
RS::get_singleton()->mesh_add_surface_from_arrays(sphere, RS::PRIMITIVE_TRIANGLES, arr);
474
}
475
476
EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
477
ERR_FAIL_NULL(RenderingServer::get_singleton());
478
RS::get_singleton()->free_rid(sphere);
479
RS::get_singleton()->free_rid(sphere_instance);
480
RS::get_singleton()->free_rid(viewport);
481
RS::get_singleton()->free_rid(light);
482
RS::get_singleton()->free_rid(light_instance);
483
RS::get_singleton()->free_rid(light2);
484
RS::get_singleton()->free_rid(light_instance2);
485
RS::get_singleton()->free_rid(camera);
486
RS::get_singleton()->free_rid(camera_attributes);
487
RS::get_singleton()->free_rid(scenario);
488
}
489
490
///////////////////////////////////////////////////////////////////////////
491
492
bool EditorScriptPreviewPlugin::handles(const String &p_type) const {
493
return ClassDB::is_parent_class(p_type, "Script");
494
}
495
496
Ref<Texture2D> EditorScriptPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
497
Error err;
498
String code = FileAccess::get_file_as_string(p_path, &err);
499
if (err != OK) {
500
return Ref<Texture2D>();
501
}
502
503
ScriptLanguage *lang = ScriptServer::get_language_for_extension(p_path.get_extension());
504
return _generate_from_source_code(lang, code, p_size, p_metadata);
505
}
506
507
Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
508
Ref<Script> scr = p_from;
509
if (scr.is_null()) {
510
return Ref<Texture2D>();
511
}
512
513
String code = scr->get_source_code().strip_edges();
514
return _generate_from_source_code(scr->get_language(), code, p_size, p_metadata);
515
}
516
517
Ref<Texture2D> EditorScriptPreviewPlugin::_generate_from_source_code(const ScriptLanguage *p_language, const String &p_source_code, const Size2 &p_size, Dictionary &p_metadata) const {
518
if (p_source_code.is_empty()) {
519
return Ref<Texture2D>();
520
}
521
522
HashSet<String> control_flow_keywords;
523
HashSet<String> keywords;
524
525
if (p_language) {
526
for (const String &keyword : p_language->get_reserved_words()) {
527
if (p_language->is_control_flow_keyword(keyword)) {
528
control_flow_keywords.insert(keyword);
529
} else {
530
keywords.insert(keyword);
531
}
532
}
533
}
534
535
int line = 0;
536
int col = 0;
537
int thumbnail_size = MAX(p_size.x, p_size.y);
538
Ref<Image> img = Image::create_empty(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8);
539
540
Color bg_color = EDITOR_GET("text_editor/theme/highlighting/background_color");
541
Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
542
Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
543
Color text_color = EDITOR_GET("text_editor/theme/highlighting/text_color");
544
Color symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
545
Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
546
Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");
547
548
if (bg_color.a == 0) {
549
bg_color = Color(0, 0, 0, 0);
550
}
551
bg_color.a = MAX(bg_color.a, 0.2); // Ensure we have some background, regardless of the text editor setting.
552
553
img->fill(bg_color);
554
555
const int x0 = thumbnail_size / 8;
556
const int y0 = thumbnail_size / 8;
557
const int available_height = thumbnail_size - 2 * y0;
558
col = x0;
559
560
bool prev_is_text = false;
561
bool in_control_flow_keyword = false;
562
bool in_keyword = false;
563
bool in_comment = false;
564
bool in_doc_comment = false;
565
for (int i = 0; i < p_source_code.length(); i++) {
566
char32_t c = p_source_code[i];
567
if (c > 32) {
568
if (col < thumbnail_size) {
569
Color color = text_color;
570
571
if (c == '#') {
572
if (i < p_source_code.length() - 1 && p_source_code[i + 1] == '#') {
573
in_doc_comment = true;
574
} else {
575
in_comment = true;
576
}
577
}
578
579
if (in_comment) {
580
color = comment_color;
581
} else if (in_doc_comment) {
582
color = doc_comment_color;
583
} else {
584
if (is_symbol(c)) {
585
// Make symbol a little visible.
586
color = symbol_color;
587
in_control_flow_keyword = false;
588
in_keyword = false;
589
} else if (!prev_is_text && is_ascii_identifier_char(c)) {
590
int pos = i;
591
592
while (is_ascii_identifier_char(p_source_code[pos])) {
593
pos++;
594
}
595
String word = p_source_code.substr(i, pos - i);
596
if (control_flow_keywords.has(word)) {
597
in_control_flow_keyword = true;
598
} else if (keywords.has(word)) {
599
in_keyword = true;
600
}
601
602
} else if (!is_ascii_identifier_char(c)) {
603
in_keyword = false;
604
}
605
606
if (in_control_flow_keyword) {
607
color = control_flow_keyword_color;
608
} else if (in_keyword) {
609
color = keyword_color;
610
}
611
}
612
Color ul = color;
613
ul.a *= 0.5;
614
img->set_pixel(col, y0 + line * 2, bg_color.blend(ul));
615
img->set_pixel(col, y0 + line * 2 + 1, color);
616
617
prev_is_text = is_ascii_identifier_char(c);
618
}
619
col++;
620
} else {
621
prev_is_text = false;
622
in_control_flow_keyword = false;
623
in_keyword = false;
624
625
if (c == '\n') {
626
in_comment = false;
627
in_doc_comment = false;
628
629
col = x0;
630
line++;
631
if (line >= available_height / 2) {
632
break;
633
}
634
} else if (c == '\t') {
635
col += 3;
636
} else {
637
col++;
638
}
639
}
640
}
641
post_process_preview(img);
642
return ImageTexture::create_from_image(img);
643
}
644
645
///////////////////////////////////////////////////////////////////
646
647
bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const {
648
return ClassDB::is_parent_class(p_type, "AudioStream");
649
}
650
651
Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
652
Ref<AudioStream> stream = p_from;
653
ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>());
654
655
Vector<uint8_t> img;
656
657
int w = p_size.x;
658
int h = p_size.y;
659
img.resize(w * h * 3);
660
661
uint8_t *imgdata = img.ptrw();
662
uint8_t *imgw = imgdata;
663
664
Ref<AudioStreamPlayback> playback = stream->instantiate_playback();
665
ERR_FAIL_COND_V(playback.is_null(), Ref<Texture2D>());
666
667
real_t len_s = stream->get_length();
668
if (len_s == 0) {
669
len_s = 60; //one minute audio if no length specified
670
}
671
int frame_length = AudioServer::get_singleton()->get_mix_rate() * len_s;
672
673
Vector<AudioFrame> frames;
674
frames.resize(frame_length);
675
676
playback->start();
677
playback->mix(frames.ptrw(), 1, frames.size());
678
playback->stop();
679
680
for (int i = 0; i < w; i++) {
681
real_t max = -1000;
682
real_t min = 1000;
683
int from = uint64_t(i) * frame_length / w;
684
int to = (uint64_t(i) + 1) * frame_length / w;
685
to = MIN(to, frame_length);
686
from = MIN(from, frame_length - 1);
687
if (to == from) {
688
to = from + 1;
689
}
690
691
for (int j = from; j < to; j++) {
692
max = MAX(max, frames[j].left);
693
max = MAX(max, frames[j].right);
694
695
min = MIN(min, frames[j].left);
696
min = MIN(min, frames[j].right);
697
}
698
699
int pfrom = CLAMP((min * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
700
int pto = CLAMP((max * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
701
702
for (int j = 0; j < h; j++) {
703
uint8_t *p = &imgw[(j * w + i) * 3];
704
if (j < pfrom || j > pto) {
705
p[0] = 100;
706
p[1] = 100;
707
p[2] = 100;
708
} else {
709
p[0] = 180;
710
p[1] = 180;
711
p[2] = 180;
712
}
713
}
714
}
715
716
p_metadata["length"] = stream->get_length();
717
718
//post_process_preview(img);
719
720
Ref<Image> image = Image::create_from_data(w, h, false, Image::FORMAT_RGB8, img);
721
return ImageTexture::create_from_image(image);
722
}
723
724
///////////////////////////////////////////////////////////////////////////
725
726
void EditorMeshPreviewPlugin::abort() {
727
draw_requester.abort();
728
}
729
730
bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
731
return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
732
}
733
734
Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
735
Ref<Mesh> mesh = p_from;
736
ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>());
737
738
RS::get_singleton()->instance_set_base(mesh_instance, mesh->get_rid());
739
740
AABB aabb = mesh->get_aabb();
741
Vector3 ofs = aabb.get_center();
742
aabb.position -= ofs;
743
Transform3D xform;
744
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math::PI * 0.125);
745
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math::PI * 0.125) * xform.basis;
746
AABB rot_aabb = xform.xform(aabb);
747
real_t m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
748
if (m == 0) {
749
return Ref<Texture2D>();
750
}
751
m = 1.0 / m;
752
m *= 0.5;
753
xform.basis.scale(Vector3(m, m, m));
754
xform.origin = -xform.basis.xform(ofs); //-ofs*m;
755
xform.origin.z -= rot_aabb.size.z * 2;
756
RS::get_singleton()->instance_set_transform(mesh_instance, xform);
757
758
draw_requester.request_and_wait(viewport);
759
760
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
761
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
762
763
RS::get_singleton()->instance_set_base(mesh_instance, RID());
764
765
img->convert(Image::FORMAT_RGBA8);
766
767
Vector2 new_size = img->get_size();
768
if (new_size.x > p_size.x) {
769
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
770
}
771
if (new_size.y > p_size.y) {
772
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
773
}
774
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
775
post_process_preview(img);
776
777
return ImageTexture::create_from_image(img);
778
}
779
780
EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
781
scenario = RS::get_singleton()->scenario_create();
782
783
viewport = RS::get_singleton()->viewport_create();
784
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
785
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
786
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
787
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
788
RS::get_singleton()->viewport_set_active(viewport, true);
789
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
790
791
camera = RS::get_singleton()->camera_create();
792
RS::get_singleton()->viewport_attach_camera(viewport, camera);
793
RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3)));
794
//RS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
795
RS::get_singleton()->camera_set_orthogonal(camera, 1.0, 0.01, 1000.0);
796
797
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
798
camera_attributes = RS::get_singleton()->camera_attributes_create();
799
RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
800
RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes);
801
}
802
803
light = RS::get_singleton()->directional_light_create();
804
light_instance = RS::get_singleton()->instance_create2(light, scenario);
805
RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
806
807
light2 = RS::get_singleton()->directional_light_create();
808
RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
809
//RS::get_singleton()->light_set_color(light2, RS::LIGHT_COLOR_SPECULAR, Color(0.0, 0.0, 0.0));
810
light_instance2 = RS::get_singleton()->instance_create2(light2, scenario);
811
812
RS::get_singleton()->instance_set_transform(light_instance2, Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
813
814
//sphere = RS::get_singleton()->mesh_create();
815
mesh_instance = RS::get_singleton()->instance_create();
816
RS::get_singleton()->instance_set_scenario(mesh_instance, scenario);
817
}
818
819
EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
820
ERR_FAIL_NULL(RenderingServer::get_singleton());
821
//RS::get_singleton()->free(sphere);
822
RS::get_singleton()->free_rid(mesh_instance);
823
RS::get_singleton()->free_rid(viewport);
824
RS::get_singleton()->free_rid(light);
825
RS::get_singleton()->free_rid(light_instance);
826
RS::get_singleton()->free_rid(light2);
827
RS::get_singleton()->free_rid(light_instance2);
828
RS::get_singleton()->free_rid(camera);
829
RS::get_singleton()->free_rid(camera_attributes);
830
RS::get_singleton()->free_rid(scenario);
831
}
832
833
///////////////////////////////////////////////////////////////////////////
834
835
void EditorFontPreviewPlugin::abort() {
836
draw_requester.abort();
837
}
838
839
bool EditorFontPreviewPlugin::handles(const String &p_type) const {
840
return ClassDB::is_parent_class(p_type, "Font");
841
}
842
843
Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
844
Ref<Font> sampled_font = ResourceLoader::load(p_path);
845
ERR_FAIL_COND_V(sampled_font.is_null(), Ref<Texture2D>());
846
847
String sample;
848
static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
849
for (int i = 0; i < sample_base.length(); i++) {
850
if (sampled_font->has_char(sample_base[i])) {
851
sample += sample_base[i];
852
}
853
}
854
if (sample.is_empty()) {
855
sample = sampled_font->get_supported_chars().substr(0, 6);
856
}
857
Vector2 size = sampled_font->get_string_size(sample, HORIZONTAL_ALIGNMENT_LEFT, -1, 50);
858
859
Vector2 pos;
860
861
pos.x = 64 - size.x / 2;
862
pos.y = 80;
863
864
const Color c = GLOBAL_GET("rendering/environment/defaults/default_clear_color");
865
const float fg = c.get_luminance() < 0.5 ? 1.0 : 0.0;
866
sampled_font->draw_string(canvas_item, pos, sample, HORIZONTAL_ALIGNMENT_LEFT, -1.f, 50, Color(fg, fg, fg));
867
868
draw_requester.request_and_wait(viewport);
869
870
RS::get_singleton()->canvas_item_clear(canvas_item);
871
872
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
873
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
874
875
img->convert(Image::FORMAT_RGBA8);
876
877
Vector2 new_size = img->get_size();
878
if (new_size.x > p_size.x) {
879
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
880
}
881
if (new_size.y > p_size.y) {
882
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
883
}
884
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
885
post_process_preview(img);
886
887
return ImageTexture::create_from_image(img);
888
}
889
890
Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
891
String path = p_from->get_path();
892
if (!FileAccess::exists(path)) {
893
return Ref<Texture2D>();
894
}
895
return generate_from_path(path, p_size, p_metadata);
896
}
897
898
EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
899
viewport = RS::get_singleton()->viewport_create();
900
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
901
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
902
RS::get_singleton()->viewport_set_active(viewport, true);
903
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
904
905
canvas = RS::get_singleton()->canvas_create();
906
canvas_item = RS::get_singleton()->canvas_item_create();
907
908
RS::get_singleton()->viewport_attach_canvas(viewport, canvas);
909
RS::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
910
}
911
912
EditorFontPreviewPlugin::~EditorFontPreviewPlugin() {
913
ERR_FAIL_NULL(RenderingServer::get_singleton());
914
RS::get_singleton()->free_rid(canvas_item);
915
RS::get_singleton()->free_rid(canvas);
916
RS::get_singleton()->free_rid(viewport);
917
}
918
919
////////////////////////////////////////////////////////////////////////////
920
921
static const real_t GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR = 4.0;
922
923
bool EditorGradientPreviewPlugin::handles(const String &p_type) const {
924
return ClassDB::is_parent_class(p_type, "Gradient");
925
}
926
927
bool EditorGradientPreviewPlugin::generate_small_preview_automatically() const {
928
return true;
929
}
930
931
Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
932
Ref<Gradient> gradient = p_from;
933
if (gradient.is_valid()) {
934
Ref<GradientTexture1D> ptex;
935
ptex.instantiate();
936
ptex->set_width(p_size.width * GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR * EDSCALE);
937
ptex->set_gradient(gradient);
938
return ImageTexture::create_from_image(ptex->get_image());
939
}
940
return Ref<Texture2D>();
941
}
942
943