Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/inspector/editor_preview_plugins.cpp
9902 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<Image>();
196
}
197
198
img = img->duplicate();
199
img->clear_mipmaps();
200
201
if (img->is_compressed()) {
202
if (img->decompress() != OK) {
203
return Ref<Image>();
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
235
if (bm->get_size() == Size2()) {
236
return Ref<Texture2D>();
237
}
238
239
Vector<uint8_t> data;
240
241
data.resize(bm->get_size().width * bm->get_size().height);
242
243
{
244
uint8_t *w = data.ptrw();
245
246
for (int i = 0; i < bm->get_size().width; i++) {
247
for (int j = 0; j < bm->get_size().height; j++) {
248
if (bm->get_bit(i, j)) {
249
w[j * (int)bm->get_size().width + i] = 255;
250
} else {
251
w[j * (int)bm->get_size().width + i] = 0;
252
}
253
}
254
}
255
}
256
257
Ref<Image> img = Image::create_from_data(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data);
258
259
if (img->is_compressed()) {
260
if (img->decompress() != OK) {
261
return Ref<Texture2D>();
262
}
263
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
264
img->convert(Image::FORMAT_RGBA8);
265
}
266
267
Vector2 new_size = img->get_size();
268
if (new_size.x > p_size.x) {
269
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
270
}
271
if (new_size.y > p_size.y) {
272
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
273
}
274
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
275
post_process_preview(img);
276
277
return ImageTexture::create_from_image(img);
278
}
279
280
bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const {
281
return true;
282
}
283
284
///////////////////////////////////////////////////////////////////////////
285
286
bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const {
287
return ClassDB::is_parent_class(p_type, "PackedScene");
288
}
289
290
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
291
return generate_from_path(p_from->get_path(), p_size, p_metadata);
292
}
293
294
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
295
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
296
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
297
cache_base = temp_path.path_join("resthumb-" + cache_base);
298
299
//does not have it, try to load a cached thumbnail
300
301
String path = cache_base + ".png";
302
303
if (!FileAccess::exists(path)) {
304
return Ref<Texture2D>();
305
}
306
307
Ref<Image> img;
308
img.instantiate();
309
Error err = img->load(path);
310
if (err == OK) {
311
post_process_preview(img);
312
return ImageTexture::create_from_image(img);
313
314
} else {
315
return Ref<Texture2D>();
316
}
317
}
318
319
//////////////////////////////////////////////////////////////////
320
321
void EditorMaterialPreviewPlugin::abort() {
322
draw_requester.abort();
323
}
324
325
bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
326
return ClassDB::is_parent_class(p_type, "Material"); // Any material.
327
}
328
329
bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
330
return true;
331
}
332
333
Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
334
Ref<Material> material = p_from;
335
ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>());
336
337
if (material->get_shader_mode() == Shader::MODE_SPATIAL) {
338
RS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
339
340
draw_requester.request_and_wait(viewport);
341
342
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
343
RS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
344
345
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
346
347
img->convert(Image::FORMAT_RGBA8);
348
int thumbnail_size = MAX(p_size.x, p_size.y);
349
img->resize(thumbnail_size, thumbnail_size, Image::INTERPOLATE_CUBIC);
350
post_process_preview(img);
351
return ImageTexture::create_from_image(img);
352
}
353
354
return Ref<Texture2D>();
355
}
356
357
EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
358
scenario = RS::get_singleton()->scenario_create();
359
360
viewport = RS::get_singleton()->viewport_create();
361
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
362
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
363
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
364
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
365
RS::get_singleton()->viewport_set_active(viewport, true);
366
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
367
368
camera = RS::get_singleton()->camera_create();
369
RS::get_singleton()->viewport_attach_camera(viewport, camera);
370
RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3)));
371
RS::get_singleton()->camera_set_perspective(camera, 45, 0.1, 10);
372
373
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
374
camera_attributes = RS::get_singleton()->camera_attributes_create();
375
RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
376
RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes);
377
}
378
379
light = RS::get_singleton()->directional_light_create();
380
light_instance = RS::get_singleton()->instance_create2(light, scenario);
381
RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
382
383
light2 = RS::get_singleton()->directional_light_create();
384
RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
385
//RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
386
387
light_instance2 = RS::get_singleton()->instance_create2(light2, scenario);
388
389
RS::get_singleton()->instance_set_transform(light_instance2, Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
390
391
sphere = RS::get_singleton()->mesh_create();
392
sphere_instance = RS::get_singleton()->instance_create2(sphere, scenario);
393
394
int lats = 32;
395
int lons = 32;
396
const double lat_step = Math::TAU / lats;
397
const double lon_step = Math::TAU / lons;
398
real_t radius = 1.0;
399
400
Vector<Vector3> vertices;
401
Vector<Vector3> normals;
402
Vector<Vector2> uvs;
403
Vector<real_t> tangents;
404
Basis tt = Basis(Vector3(0, 1, 0), Math::PI * 0.5);
405
406
for (int i = 1; i <= lats; i++) {
407
double lat0 = lat_step * (i - 1) - Math::TAU / 4;
408
double z0 = Math::sin(lat0);
409
double zr0 = Math::cos(lat0);
410
411
double lat1 = lat_step * i - Math::TAU / 4;
412
double z1 = Math::sin(lat1);
413
double zr1 = Math::cos(lat1);
414
415
for (int j = lons; j >= 1; j--) {
416
double lng0 = lon_step * (j - 1);
417
double x0 = Math::cos(lng0);
418
double y0 = Math::sin(lng0);
419
420
double lng1 = lon_step * j;
421
double x1 = Math::cos(lng1);
422
double y1 = Math::sin(lng1);
423
424
Vector3 v[4] = {
425
Vector3(x1 * zr0, z0, y1 * zr0),
426
Vector3(x1 * zr1, z1, y1 * zr1),
427
Vector3(x0 * zr1, z1, y0 * zr1),
428
Vector3(x0 * zr0, z0, y0 * zr0)
429
};
430
431
#define ADD_POINT(m_idx) \
432
normals.push_back(v[m_idx]); \
433
vertices.push_back(v[m_idx] * radius); \
434
{ \
435
Vector2 uv(Math::atan2(v[m_idx].x, v[m_idx].z), Math::atan2(-v[m_idx].y, v[m_idx].z)); \
436
uv /= Math::PI; \
437
uv *= 4.0; \
438
uv = uv * 0.5 + Vector2(0.5, 0.5); \
439
uvs.push_back(uv); \
440
} \
441
{ \
442
Vector3 t = tt.xform(v[m_idx]); \
443
tangents.push_back(t.x); \
444
tangents.push_back(t.y); \
445
tangents.push_back(t.z); \
446
tangents.push_back(1.0); \
447
}
448
449
ADD_POINT(0);
450
ADD_POINT(1);
451
ADD_POINT(2);
452
453
ADD_POINT(2);
454
ADD_POINT(3);
455
ADD_POINT(0);
456
}
457
}
458
459
Array arr;
460
arr.resize(RS::ARRAY_MAX);
461
arr[RS::ARRAY_VERTEX] = vertices;
462
arr[RS::ARRAY_NORMAL] = normals;
463
arr[RS::ARRAY_TANGENT] = tangents;
464
arr[RS::ARRAY_TEX_UV] = uvs;
465
RS::get_singleton()->mesh_add_surface_from_arrays(sphere, RS::PRIMITIVE_TRIANGLES, arr);
466
}
467
468
EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
469
ERR_FAIL_NULL(RenderingServer::get_singleton());
470
RS::get_singleton()->free(sphere);
471
RS::get_singleton()->free(sphere_instance);
472
RS::get_singleton()->free(viewport);
473
RS::get_singleton()->free(light);
474
RS::get_singleton()->free(light_instance);
475
RS::get_singleton()->free(light2);
476
RS::get_singleton()->free(light_instance2);
477
RS::get_singleton()->free(camera);
478
RS::get_singleton()->free(camera_attributes);
479
RS::get_singleton()->free(scenario);
480
}
481
482
///////////////////////////////////////////////////////////////////////////
483
484
bool EditorScriptPreviewPlugin::handles(const String &p_type) const {
485
return ClassDB::is_parent_class(p_type, "Script");
486
}
487
488
Ref<Texture2D> EditorScriptPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
489
Error err;
490
String code = FileAccess::get_file_as_string(p_path, &err);
491
if (err != OK) {
492
return Ref<Texture2D>();
493
}
494
495
ScriptLanguage *lang = ScriptServer::get_language_for_extension(p_path.get_extension());
496
return _generate_from_source_code(lang, code, p_size, p_metadata);
497
}
498
499
Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
500
Ref<Script> scr = p_from;
501
if (scr.is_null()) {
502
return Ref<Texture2D>();
503
}
504
505
String code = scr->get_source_code().strip_edges();
506
return _generate_from_source_code(scr->get_language(), code, p_size, p_metadata);
507
}
508
509
Ref<Texture2D> EditorScriptPreviewPlugin::_generate_from_source_code(const ScriptLanguage *p_language, const String &p_source_code, const Size2 &p_size, Dictionary &p_metadata) const {
510
if (p_source_code.is_empty()) {
511
return Ref<Texture2D>();
512
}
513
514
HashSet<String> control_flow_keywords;
515
HashSet<String> keywords;
516
517
if (p_language) {
518
for (const String &keyword : p_language->get_reserved_words()) {
519
if (p_language->is_control_flow_keyword(keyword)) {
520
control_flow_keywords.insert(keyword);
521
} else {
522
keywords.insert(keyword);
523
}
524
}
525
}
526
527
int line = 0;
528
int col = 0;
529
int thumbnail_size = MAX(p_size.x, p_size.y);
530
Ref<Image> img = Image::create_empty(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8);
531
532
Color bg_color = EDITOR_GET("text_editor/theme/highlighting/background_color");
533
Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
534
Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
535
Color text_color = EDITOR_GET("text_editor/theme/highlighting/text_color");
536
Color symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
537
Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
538
Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");
539
540
if (bg_color.a == 0) {
541
bg_color = Color(0, 0, 0, 0);
542
}
543
bg_color.a = MAX(bg_color.a, 0.2); // Ensure we have some background, regardless of the text editor setting.
544
545
img->fill(bg_color);
546
547
const int x0 = thumbnail_size / 8;
548
const int y0 = thumbnail_size / 8;
549
const int available_height = thumbnail_size - 2 * y0;
550
col = x0;
551
552
bool prev_is_text = false;
553
bool in_control_flow_keyword = false;
554
bool in_keyword = false;
555
bool in_comment = false;
556
bool in_doc_comment = false;
557
for (int i = 0; i < p_source_code.length(); i++) {
558
char32_t c = p_source_code[i];
559
if (c > 32) {
560
if (col < thumbnail_size) {
561
Color color = text_color;
562
563
if (c == '#') {
564
if (i < p_source_code.length() - 1 && p_source_code[i + 1] == '#') {
565
in_doc_comment = true;
566
} else {
567
in_comment = true;
568
}
569
}
570
571
if (in_comment) {
572
color = comment_color;
573
} else if (in_doc_comment) {
574
color = doc_comment_color;
575
} else {
576
if (is_symbol(c)) {
577
// Make symbol a little visible.
578
color = symbol_color;
579
in_control_flow_keyword = false;
580
in_keyword = false;
581
} else if (!prev_is_text && is_ascii_identifier_char(c)) {
582
int pos = i;
583
584
while (is_ascii_identifier_char(p_source_code[pos])) {
585
pos++;
586
}
587
String word = p_source_code.substr(i, pos - i);
588
if (control_flow_keywords.has(word)) {
589
in_control_flow_keyword = true;
590
} else if (keywords.has(word)) {
591
in_keyword = true;
592
}
593
594
} else if (!is_ascii_identifier_char(c)) {
595
in_keyword = false;
596
}
597
598
if (in_control_flow_keyword) {
599
color = control_flow_keyword_color;
600
} else if (in_keyword) {
601
color = keyword_color;
602
}
603
}
604
Color ul = color;
605
ul.a *= 0.5;
606
img->set_pixel(col, y0 + line * 2, bg_color.blend(ul));
607
img->set_pixel(col, y0 + line * 2 + 1, color);
608
609
prev_is_text = is_ascii_identifier_char(c);
610
}
611
col++;
612
} else {
613
prev_is_text = false;
614
in_control_flow_keyword = false;
615
in_keyword = false;
616
617
if (c == '\n') {
618
in_comment = false;
619
in_doc_comment = false;
620
621
col = x0;
622
line++;
623
if (line >= available_height / 2) {
624
break;
625
}
626
} else if (c == '\t') {
627
col += 3;
628
} else {
629
col++;
630
}
631
}
632
}
633
post_process_preview(img);
634
return ImageTexture::create_from_image(img);
635
}
636
637
///////////////////////////////////////////////////////////////////
638
639
bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const {
640
return ClassDB::is_parent_class(p_type, "AudioStream");
641
}
642
643
Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
644
Ref<AudioStream> stream = p_from;
645
ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>());
646
647
Vector<uint8_t> img;
648
649
int w = p_size.x;
650
int h = p_size.y;
651
img.resize(w * h * 3);
652
653
uint8_t *imgdata = img.ptrw();
654
uint8_t *imgw = imgdata;
655
656
Ref<AudioStreamPlayback> playback = stream->instantiate_playback();
657
ERR_FAIL_COND_V(playback.is_null(), Ref<Texture2D>());
658
659
real_t len_s = stream->get_length();
660
if (len_s == 0) {
661
len_s = 60; //one minute audio if no length specified
662
}
663
int frame_length = AudioServer::get_singleton()->get_mix_rate() * len_s;
664
665
Vector<AudioFrame> frames;
666
frames.resize(frame_length);
667
668
playback->start();
669
playback->mix(frames.ptrw(), 1, frames.size());
670
playback->stop();
671
672
for (int i = 0; i < w; i++) {
673
real_t max = -1000;
674
real_t min = 1000;
675
int from = uint64_t(i) * frame_length / w;
676
int to = (uint64_t(i) + 1) * frame_length / w;
677
to = MIN(to, frame_length);
678
from = MIN(from, frame_length - 1);
679
if (to == from) {
680
to = from + 1;
681
}
682
683
for (int j = from; j < to; j++) {
684
max = MAX(max, frames[j].left);
685
max = MAX(max, frames[j].right);
686
687
min = MIN(min, frames[j].left);
688
min = MIN(min, frames[j].right);
689
}
690
691
int pfrom = CLAMP((min * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
692
int pto = CLAMP((max * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
693
694
for (int j = 0; j < h; j++) {
695
uint8_t *p = &imgw[(j * w + i) * 3];
696
if (j < pfrom || j > pto) {
697
p[0] = 100;
698
p[1] = 100;
699
p[2] = 100;
700
} else {
701
p[0] = 180;
702
p[1] = 180;
703
p[2] = 180;
704
}
705
}
706
}
707
708
p_metadata["length"] = stream->get_length();
709
710
//post_process_preview(img);
711
712
Ref<Image> image = Image::create_from_data(w, h, false, Image::FORMAT_RGB8, img);
713
return ImageTexture::create_from_image(image);
714
}
715
716
///////////////////////////////////////////////////////////////////////////
717
718
void EditorMeshPreviewPlugin::abort() {
719
draw_requester.abort();
720
}
721
722
bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
723
return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
724
}
725
726
Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
727
Ref<Mesh> mesh = p_from;
728
ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>());
729
730
RS::get_singleton()->instance_set_base(mesh_instance, mesh->get_rid());
731
732
AABB aabb = mesh->get_aabb();
733
Vector3 ofs = aabb.get_center();
734
aabb.position -= ofs;
735
Transform3D xform;
736
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math::PI * 0.125);
737
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math::PI * 0.125) * xform.basis;
738
AABB rot_aabb = xform.xform(aabb);
739
real_t m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
740
if (m == 0) {
741
return Ref<Texture2D>();
742
}
743
m = 1.0 / m;
744
m *= 0.5;
745
xform.basis.scale(Vector3(m, m, m));
746
xform.origin = -xform.basis.xform(ofs); //-ofs*m;
747
xform.origin.z -= rot_aabb.size.z * 2;
748
RS::get_singleton()->instance_set_transform(mesh_instance, xform);
749
750
draw_requester.request_and_wait(viewport);
751
752
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
753
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
754
755
RS::get_singleton()->instance_set_base(mesh_instance, RID());
756
757
img->convert(Image::FORMAT_RGBA8);
758
759
Vector2 new_size = img->get_size();
760
if (new_size.x > p_size.x) {
761
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
762
}
763
if (new_size.y > p_size.y) {
764
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
765
}
766
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
767
post_process_preview(img);
768
769
return ImageTexture::create_from_image(img);
770
}
771
772
EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
773
scenario = RS::get_singleton()->scenario_create();
774
775
viewport = RS::get_singleton()->viewport_create();
776
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
777
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
778
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
779
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
780
RS::get_singleton()->viewport_set_active(viewport, true);
781
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
782
783
camera = RS::get_singleton()->camera_create();
784
RS::get_singleton()->viewport_attach_camera(viewport, camera);
785
RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3)));
786
//RS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
787
RS::get_singleton()->camera_set_orthogonal(camera, 1.0, 0.01, 1000.0);
788
789
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
790
camera_attributes = RS::get_singleton()->camera_attributes_create();
791
RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
792
RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes);
793
}
794
795
light = RS::get_singleton()->directional_light_create();
796
light_instance = RS::get_singleton()->instance_create2(light, scenario);
797
RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
798
799
light2 = RS::get_singleton()->directional_light_create();
800
RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
801
//RS::get_singleton()->light_set_color(light2, RS::LIGHT_COLOR_SPECULAR, Color(0.0, 0.0, 0.0));
802
light_instance2 = RS::get_singleton()->instance_create2(light2, scenario);
803
804
RS::get_singleton()->instance_set_transform(light_instance2, Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
805
806
//sphere = RS::get_singleton()->mesh_create();
807
mesh_instance = RS::get_singleton()->instance_create();
808
RS::get_singleton()->instance_set_scenario(mesh_instance, scenario);
809
}
810
811
EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
812
ERR_FAIL_NULL(RenderingServer::get_singleton());
813
//RS::get_singleton()->free(sphere);
814
RS::get_singleton()->free(mesh_instance);
815
RS::get_singleton()->free(viewport);
816
RS::get_singleton()->free(light);
817
RS::get_singleton()->free(light_instance);
818
RS::get_singleton()->free(light2);
819
RS::get_singleton()->free(light_instance2);
820
RS::get_singleton()->free(camera);
821
RS::get_singleton()->free(camera_attributes);
822
RS::get_singleton()->free(scenario);
823
}
824
825
///////////////////////////////////////////////////////////////////////////
826
827
void EditorFontPreviewPlugin::abort() {
828
draw_requester.abort();
829
}
830
831
bool EditorFontPreviewPlugin::handles(const String &p_type) const {
832
return ClassDB::is_parent_class(p_type, "Font");
833
}
834
835
Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
836
Ref<Font> sampled_font = ResourceLoader::load(p_path);
837
ERR_FAIL_COND_V(sampled_font.is_null(), Ref<Texture2D>());
838
839
String sample;
840
static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
841
for (int i = 0; i < sample_base.length(); i++) {
842
if (sampled_font->has_char(sample_base[i])) {
843
sample += sample_base[i];
844
}
845
}
846
if (sample.is_empty()) {
847
sample = sampled_font->get_supported_chars().substr(0, 6);
848
}
849
Vector2 size = sampled_font->get_string_size(sample, HORIZONTAL_ALIGNMENT_LEFT, -1, 50);
850
851
Vector2 pos;
852
853
pos.x = 64 - size.x / 2;
854
pos.y = 80;
855
856
const Color c = GLOBAL_GET("rendering/environment/defaults/default_clear_color");
857
const float fg = c.get_luminance() < 0.5 ? 1.0 : 0.0;
858
sampled_font->draw_string(canvas_item, pos, sample, HORIZONTAL_ALIGNMENT_LEFT, -1.f, 50, Color(fg, fg, fg));
859
860
draw_requester.request_and_wait(viewport);
861
862
RS::get_singleton()->canvas_item_clear(canvas_item);
863
864
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
865
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
866
867
img->convert(Image::FORMAT_RGBA8);
868
869
Vector2 new_size = img->get_size();
870
if (new_size.x > p_size.x) {
871
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
872
}
873
if (new_size.y > p_size.y) {
874
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
875
}
876
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
877
post_process_preview(img);
878
879
return ImageTexture::create_from_image(img);
880
}
881
882
Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
883
String path = p_from->get_path();
884
if (!FileAccess::exists(path)) {
885
return Ref<Texture2D>();
886
}
887
return generate_from_path(path, p_size, p_metadata);
888
}
889
890
EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
891
viewport = RS::get_singleton()->viewport_create();
892
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
893
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
894
RS::get_singleton()->viewport_set_active(viewport, true);
895
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
896
897
canvas = RS::get_singleton()->canvas_create();
898
canvas_item = RS::get_singleton()->canvas_item_create();
899
900
RS::get_singleton()->viewport_attach_canvas(viewport, canvas);
901
RS::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
902
}
903
904
EditorFontPreviewPlugin::~EditorFontPreviewPlugin() {
905
ERR_FAIL_NULL(RenderingServer::get_singleton());
906
RS::get_singleton()->free(canvas_item);
907
RS::get_singleton()->free(canvas);
908
RS::get_singleton()->free(viewport);
909
}
910
911
////////////////////////////////////////////////////////////////////////////
912
913
static const real_t GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR = 4.0;
914
915
bool EditorGradientPreviewPlugin::handles(const String &p_type) const {
916
return ClassDB::is_parent_class(p_type, "Gradient");
917
}
918
919
bool EditorGradientPreviewPlugin::generate_small_preview_automatically() const {
920
return true;
921
}
922
923
Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
924
Ref<Gradient> gradient = p_from;
925
if (gradient.is_valid()) {
926
Ref<GradientTexture1D> ptex;
927
ptex.instantiate();
928
ptex->set_width(p_size.width * GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR * EDSCALE);
929
ptex->set_gradient(gradient);
930
return ImageTexture::create_from_image(ptex->get_image());
931
}
932
return Ref<Texture2D>();
933
}
934
935