Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/resources/compressed_texture.cpp
20873 views
1
/**************************************************************************/
2
/* compressed_texture.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 "compressed_texture.h"
32
33
#include "core/io/file_access.h"
34
#include "scene/resources/bit_map.h"
35
36
Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
37
alpha_cache.unref();
38
39
ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
40
41
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
42
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
43
44
uint8_t header[4];
45
f->get_buffer(header, 4);
46
if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') {
47
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header).");
48
}
49
50
uint32_t version = f->get_32();
51
52
if (version > FORMAT_VERSION) {
53
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
54
}
55
r_width = f->get_32();
56
r_height = f->get_32();
57
uint32_t df = f->get_32(); //data format
58
59
//skip reserved
60
mipmap_limit = int(f->get_32());
61
//reserved
62
f->get_32();
63
f->get_32();
64
f->get_32();
65
66
#ifdef TOOLS_ENABLED
67
68
r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D;
69
r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS;
70
r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL;
71
72
#else
73
74
r_request_3d = false;
75
r_request_roughness = false;
76
r_request_normal = false;
77
78
#endif
79
if (!(df & FORMAT_BIT_STREAM)) {
80
p_size_limit = 0;
81
}
82
83
image = load_image_from_file(f, p_size_limit);
84
85
if (image.is_null() || image->is_empty()) {
86
return ERR_CANT_OPEN;
87
}
88
89
return OK;
90
}
91
92
void CompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
93
if (texture.is_valid()) {
94
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
95
}
96
97
Resource::set_path(p_path, p_take_over);
98
}
99
100
void CompressedTexture2D::_requested_3d(void *p_ud) {
101
CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
102
Ref<CompressedTexture2D> ctex(ct);
103
ERR_FAIL_NULL(request_3d_callback);
104
request_3d_callback(ctex);
105
}
106
107
void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
108
CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
109
Ref<CompressedTexture2D> ctex(ct);
110
ERR_FAIL_NULL(request_roughness_callback);
111
request_roughness_callback(ctex, p_normal_path, p_roughness_channel);
112
}
113
114
void CompressedTexture2D::_requested_normal(void *p_ud) {
115
CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
116
Ref<CompressedTexture2D> ctex(ct);
117
ERR_FAIL_NULL(request_normal_callback);
118
request_normal_callback(ctex);
119
}
120
121
CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr;
122
CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr;
123
CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr;
124
125
Image::Format CompressedTexture2D::get_format() const {
126
return format;
127
}
128
129
Error CompressedTexture2D::load(const String &p_path) {
130
int lw, lh;
131
Ref<Image> image;
132
image.instantiate();
133
134
bool request_3d;
135
bool request_normal;
136
bool request_roughness;
137
int mipmap_limit;
138
139
Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);
140
if (err) {
141
return err;
142
}
143
144
if (texture.is_valid()) {
145
RID new_texture = RS::get_singleton()->texture_2d_create(image);
146
RS::get_singleton()->texture_replace(texture, new_texture);
147
} else {
148
texture = RS::get_singleton()->texture_2d_create(image);
149
}
150
if (lw || lh) {
151
RS::get_singleton()->texture_set_size_override(texture, lw, lh);
152
}
153
154
w = lw;
155
h = lh;
156
path_to_file = p_path;
157
format = image->get_format();
158
159
if (get_path().is_empty()) {
160
//temporarily set path if no path set for resource, helps find errors
161
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
162
}
163
164
#ifdef TOOLS_ENABLED
165
166
if (request_3d) {
167
//print_line("request detect 3D at " + p_path);
168
RS::get_singleton()->texture_set_detect_3d_callback(texture, _requested_3d, this);
169
} else {
170
//print_line("not requesting detect 3D at " + p_path);
171
RS::get_singleton()->texture_set_detect_3d_callback(texture, nullptr, nullptr);
172
}
173
174
if (request_roughness) {
175
//print_line("request detect srgb at " + p_path);
176
RS::get_singleton()->texture_set_detect_roughness_callback(texture, _requested_roughness, this);
177
} else {
178
//print_line("not requesting detect srgb at " + p_path);
179
RS::get_singleton()->texture_set_detect_roughness_callback(texture, nullptr, nullptr);
180
}
181
182
if (request_normal) {
183
//print_line("request detect srgb at " + p_path);
184
RS::get_singleton()->texture_set_detect_normal_callback(texture, _requested_normal, this);
185
} else {
186
//print_line("not requesting detect normal at " + p_path);
187
RS::get_singleton()->texture_set_detect_normal_callback(texture, nullptr, nullptr);
188
}
189
190
#endif
191
notify_property_list_changed();
192
emit_changed();
193
return OK;
194
}
195
196
String CompressedTexture2D::get_load_path() const {
197
return path_to_file;
198
}
199
200
int CompressedTexture2D::get_width() const {
201
return w;
202
}
203
204
int CompressedTexture2D::get_height() const {
205
return h;
206
}
207
208
RID CompressedTexture2D::get_rid() const {
209
if (!texture.is_valid()) {
210
texture = RS::get_singleton()->texture_2d_placeholder_create();
211
}
212
return texture;
213
}
214
215
void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
216
if ((w | h) == 0) {
217
return;
218
}
219
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
220
}
221
222
void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
223
if ((w | h) == 0) {
224
return;
225
}
226
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
227
}
228
229
void CompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
230
if ((w | h) == 0) {
231
return;
232
}
233
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
234
}
235
236
bool CompressedTexture2D::has_alpha() const {
237
return false;
238
}
239
240
Ref<Image> CompressedTexture2D::get_image() const {
241
if (texture.is_valid()) {
242
return RS::get_singleton()->texture_2d_get(texture);
243
} else {
244
return Ref<Image>();
245
}
246
}
247
248
bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
249
if (alpha_cache.is_null()) {
250
Ref<Image> img = get_image();
251
if (img.is_valid()) {
252
if (img->is_compressed()) { //must decompress, if compressed
253
Ref<Image> decom = img->duplicate();
254
decom->decompress();
255
img = decom;
256
}
257
258
alpha_cache.instantiate();
259
alpha_cache->create_from_image_alpha(img);
260
}
261
}
262
263
if (alpha_cache.is_valid()) {
264
int aw = int(alpha_cache->get_size().width);
265
int ah = int(alpha_cache->get_size().height);
266
if (aw == 0 || ah == 0) {
267
return true;
268
}
269
270
int x = p_x * aw / w;
271
int y = p_y * ah / h;
272
273
x = CLAMP(x, 0, aw - 1);
274
y = CLAMP(y, 0, ah - 1);
275
276
return alpha_cache->get_bit(x, y);
277
}
278
279
return true;
280
}
281
282
void CompressedTexture2D::reload_from_file() {
283
String path = get_path();
284
if (!path.is_resource_file()) {
285
return;
286
}
287
288
path = ResourceLoader::path_remap(path); //remap for translation
289
path = ResourceLoader::import_remap(path); //remap for import
290
if (!path.is_resource_file()) {
291
return;
292
}
293
294
load(path);
295
}
296
297
Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) {
298
uint32_t data_format = f->get_32();
299
uint32_t w = f->get_16();
300
uint32_t h = f->get_16();
301
uint32_t mipmaps = f->get_32();
302
Image::Format format = Image::Format(f->get_32());
303
304
if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) {
305
//look for a PNG or WebP file inside
306
307
int sw = w;
308
int sh = h;
309
310
//mipmaps need to be read independently, they will be later combined
311
Vector<Ref<Image>> mipmap_images;
312
uint64_t total_size = 0;
313
314
for (uint32_t i = 0; i < mipmaps + 1; i++) {
315
uint32_t size = f->get_32();
316
317
if (p_size_limit > 0 && i < (mipmaps - 1) && (sw > p_size_limit || sh > p_size_limit)) {
318
//can't load this due to size limit
319
sw = MAX(sw >> 1, 1);
320
sh = MAX(sh >> 1, 1);
321
f->seek(f->get_position() + size);
322
continue;
323
}
324
325
Vector<uint8_t> pv;
326
pv.resize(size);
327
{
328
uint8_t *wr = pv.ptrw();
329
f->get_buffer(wr, size);
330
}
331
332
Ref<Image> img;
333
if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) {
334
img = Image::png_unpacker(pv);
335
} else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) {
336
img = Image::webp_unpacker(pv);
337
}
338
339
if (img.is_null() || img->is_empty()) {
340
ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
341
}
342
// If the image is compressed and its format doesn't match the desired format, return an empty reference.
343
// This is done to avoid recompressing the image on load.
344
ERR_FAIL_COND_V(img->is_compressed() && format != img->get_format(), Ref<Image>());
345
346
// The format will actually be the format of the header,
347
// as it may have changed on compression.
348
if (format != img->get_format()) {
349
// Convert the image to the desired format.
350
// Note: We are not decompressing the image here, just changing its format.
351
// It's important that all images in the texture array share the same format for correct rendering.
352
img->convert(format);
353
}
354
355
total_size += img->get_data().size();
356
357
mipmap_images.push_back(img);
358
359
sw = MAX(sw >> 1, 1);
360
sh = MAX(sh >> 1, 1);
361
}
362
363
//print_line("mipmap read total: " + itos(mipmap_images.size()));
364
365
Ref<Image> image;
366
image.instantiate();
367
368
if (mipmap_images.size() == 1) {
369
//only one image (which will most likely be the case anyway for this format)
370
image = mipmap_images[0];
371
return image;
372
373
} else {
374
//rarer use case, but needs to be supported
375
Vector<uint8_t> img_data;
376
img_data.resize(total_size);
377
378
{
379
uint8_t *wr = img_data.ptrw();
380
381
int ofs = 0;
382
for (int i = 0; i < mipmap_images.size(); i++) {
383
Vector<uint8_t> id = mipmap_images[i]->get_data();
384
int len = id.size();
385
const uint8_t *r = id.ptr();
386
memcpy(&wr[ofs], r, len);
387
ofs += len;
388
}
389
}
390
391
image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data);
392
return image;
393
}
394
395
} else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
396
int sw = w;
397
int sh = h;
398
uint32_t size = f->get_32();
399
if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {
400
//can't load this due to size limit
401
sw = MAX(sw >> 1, 1);
402
sh = MAX(sh >> 1, 1);
403
f->seek(f->get_position() + size);
404
return Ref<Image>();
405
}
406
Vector<uint8_t> pv;
407
pv.resize(size);
408
{
409
uint8_t *wr = pv.ptrw();
410
f->get_buffer(wr, size);
411
}
412
Ref<Image> img;
413
img = Image::basis_universal_unpacker(pv);
414
if (img.is_null() || img->is_empty()) {
415
ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
416
}
417
format = img->get_format();
418
sw = MAX(sw >> 1, 1);
419
sh = MAX(sh >> 1, 1);
420
return img;
421
} else if (data_format == DATA_FORMAT_IMAGE) {
422
int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);
423
424
for (uint32_t i = 0; i < mipmaps + 1; i++) {
425
int tw, th;
426
int ofs = Image::get_image_mipmap_offset_and_dimensions(w, h, format, i, tw, th);
427
428
if (p_size_limit > 0 && i < mipmaps && (p_size_limit > tw || p_size_limit > th)) {
429
if (ofs) {
430
f->seek(f->get_position() + ofs);
431
}
432
continue; //oops, size limit enforced, go to next
433
}
434
435
Vector<uint8_t> data;
436
data.resize(size - ofs);
437
438
{
439
uint8_t *wr = data.ptrw();
440
f->get_buffer(wr, data.size());
441
}
442
443
Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data);
444
445
return image;
446
}
447
}
448
449
return Ref<Image>();
450
}
451
452
void CompressedTexture2D::_bind_methods() {
453
ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load);
454
ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path);
455
456
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
457
}
458
459
CompressedTexture2D::~CompressedTexture2D() {
460
if (texture.is_valid()) {
461
ERR_FAIL_NULL(RenderingServer::get_singleton());
462
RS::get_singleton()->free_rid(texture);
463
}
464
}
465
466
Ref<Resource> ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
467
Ref<CompressedTexture2D> st;
468
st.instantiate();
469
Error err = st->load(p_path);
470
if (r_error) {
471
*r_error = err;
472
}
473
if (err != OK) {
474
return Ref<Resource>();
475
}
476
477
return st;
478
}
479
480
void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
481
p_extensions->push_back("ctex");
482
}
483
484
bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const {
485
return p_type == "CompressedTexture2D";
486
}
487
488
String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const {
489
if (p_path.has_extension("ctex")) {
490
return "CompressedTexture2D";
491
}
492
return "";
493
}
494
495
void CompressedTexture3D::set_path(const String &p_path, bool p_take_over) {
496
if (texture.is_valid()) {
497
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
498
}
499
500
Resource::set_path(p_path, p_take_over);
501
}
502
503
Image::Format CompressedTexture3D::get_format() const {
504
return format;
505
}
506
507
Error CompressedTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
508
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
509
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
510
511
uint8_t header[4];
512
f->get_buffer(header, 4);
513
ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED);
514
515
//stored as compressed textures (used for lossless and lossy compression)
516
uint32_t version = f->get_32();
517
518
if (version > FORMAT_VERSION) {
519
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
520
}
521
522
r_depth = f->get_32(); //depth
523
f->get_32(); //ignored (mode)
524
f->get_32(); // ignored (data format)
525
526
f->get_32(); //ignored
527
int mipmap_count = f->get_32();
528
f->get_32(); //ignored
529
f->get_32(); //ignored
530
531
r_mipmaps = mipmap_count != 0;
532
533
r_data.clear();
534
535
for (int i = 0; i < (r_depth + mipmap_count); i++) {
536
Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0);
537
ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
538
if (i == 0) {
539
r_format = image->get_format();
540
r_width = image->get_width();
541
r_height = image->get_height();
542
}
543
r_data.push_back(image);
544
}
545
546
return OK;
547
}
548
549
Error CompressedTexture3D::load(const String &p_path) {
550
Vector<Ref<Image>> data;
551
552
int tw, th, td;
553
Image::Format tfmt;
554
bool tmm;
555
556
Error err = _load_data(p_path, data, tfmt, tw, th, td, tmm);
557
if (err) {
558
return err;
559
}
560
561
if (texture.is_valid()) {
562
RID new_texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
563
RS::get_singleton()->texture_replace(texture, new_texture);
564
} else {
565
texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
566
}
567
568
w = tw;
569
h = th;
570
d = td;
571
mipmaps = tmm;
572
format = tfmt;
573
574
path_to_file = p_path;
575
576
if (get_path().is_empty()) {
577
//temporarily set path if no path set for resource, helps find errors
578
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
579
}
580
581
notify_property_list_changed();
582
emit_changed();
583
return OK;
584
}
585
586
String CompressedTexture3D::get_load_path() const {
587
return path_to_file;
588
}
589
590
int CompressedTexture3D::get_width() const {
591
return w;
592
}
593
594
int CompressedTexture3D::get_height() const {
595
return h;
596
}
597
598
int CompressedTexture3D::get_depth() const {
599
return d;
600
}
601
602
bool CompressedTexture3D::has_mipmaps() const {
603
return mipmaps;
604
}
605
606
RID CompressedTexture3D::get_rid() const {
607
if (!texture.is_valid()) {
608
texture = RS::get_singleton()->texture_3d_placeholder_create();
609
}
610
return texture;
611
}
612
613
Vector<Ref<Image>> CompressedTexture3D::get_data() const {
614
if (texture.is_valid()) {
615
return RS::get_singleton()->texture_3d_get(texture);
616
} else {
617
return Vector<Ref<Image>>();
618
}
619
}
620
621
void CompressedTexture3D::reload_from_file() {
622
String path = get_path();
623
if (!path.is_resource_file()) {
624
return;
625
}
626
627
path = ResourceLoader::path_remap(path); //remap for translation
628
path = ResourceLoader::import_remap(path); //remap for import
629
if (!path.is_resource_file()) {
630
return;
631
}
632
633
load(path);
634
}
635
636
void CompressedTexture3D::_bind_methods() {
637
ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load);
638
ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path);
639
640
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
641
}
642
643
CompressedTexture3D::~CompressedTexture3D() {
644
if (texture.is_valid()) {
645
ERR_FAIL_NULL(RenderingServer::get_singleton());
646
RS::get_singleton()->free_rid(texture);
647
}
648
}
649
650
Ref<Resource> ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
651
Ref<CompressedTexture3D> st;
652
st.instantiate();
653
Error err = st->load(p_path);
654
if (r_error) {
655
*r_error = err;
656
}
657
if (err != OK) {
658
return Ref<Resource>();
659
}
660
661
return st;
662
}
663
664
void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
665
p_extensions->push_back("ctex3d");
666
}
667
668
bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const {
669
return p_type == "CompressedTexture3D";
670
}
671
672
String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const {
673
if (p_path.has_extension("ctex3d")) {
674
return "CompressedTexture3D";
675
}
676
return "";
677
}
678
679
void CompressedTextureLayered::set_path(const String &p_path, bool p_take_over) {
680
if (texture.is_valid()) {
681
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
682
}
683
684
Resource::set_path(p_path, p_take_over);
685
}
686
687
Image::Format CompressedTextureLayered::get_format() const {
688
return format;
689
}
690
691
Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
692
ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
693
694
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
695
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
696
697
uint8_t header[4];
698
f->get_buffer(header, 4);
699
if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {
700
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header).");
701
}
702
703
uint32_t version = f->get_32();
704
705
if (version > FORMAT_VERSION) {
706
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
707
}
708
709
uint32_t layer_count = f->get_32(); //layer count
710
uint32_t type = f->get_32(); //layer count
711
ERR_FAIL_COND_V((int)type != layered_type, ERR_INVALID_DATA);
712
713
uint32_t df = f->get_32(); //data format
714
mipmap_limit = int(f->get_32());
715
//reserved
716
f->get_32();
717
f->get_32();
718
f->get_32();
719
720
if (!(df & FORMAT_BIT_STREAM)) {
721
p_size_limit = 0;
722
}
723
724
images.resize(layer_count);
725
726
for (uint32_t i = 0; i < layer_count; i++) {
727
Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit);
728
ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
729
images.write[i] = image;
730
}
731
732
return OK;
733
}
734
735
Error CompressedTextureLayered::load(const String &p_path) {
736
Vector<Ref<Image>> images;
737
738
int mipmap_limit;
739
740
Error err = _load_data(p_path, images, mipmap_limit);
741
if (err) {
742
return err;
743
}
744
745
if (texture.is_valid()) {
746
RID new_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
747
RS::get_singleton()->texture_replace(texture, new_texture);
748
} else {
749
texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
750
}
751
752
w = images[0]->get_width();
753
h = images[0]->get_height();
754
mipmaps = images[0]->has_mipmaps();
755
format = images[0]->get_format();
756
layers = images.size();
757
758
path_to_file = p_path;
759
760
if (get_path().is_empty()) {
761
//temporarily set path if no path set for resource, helps find errors
762
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
763
}
764
765
notify_property_list_changed();
766
emit_changed();
767
return OK;
768
}
769
770
String CompressedTextureLayered::get_load_path() const {
771
return path_to_file;
772
}
773
774
int CompressedTextureLayered::get_width() const {
775
return w;
776
}
777
778
int CompressedTextureLayered::get_height() const {
779
return h;
780
}
781
782
int CompressedTextureLayered::get_layers() const {
783
return layers;
784
}
785
786
bool CompressedTextureLayered::has_mipmaps() const {
787
return mipmaps;
788
}
789
790
TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const {
791
return layered_type;
792
}
793
794
RID CompressedTextureLayered::get_rid() const {
795
if (!texture.is_valid()) {
796
texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
797
}
798
return texture;
799
}
800
801
Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {
802
if (texture.is_valid()) {
803
ERR_FAIL_INDEX_V(p_layer, get_layers(), Ref<Image>());
804
return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
805
} else {
806
return Ref<Image>();
807
}
808
}
809
810
void CompressedTextureLayered::reload_from_file() {
811
String path = get_path();
812
if (!path.is_resource_file()) {
813
return;
814
}
815
816
path = ResourceLoader::path_remap(path); //remap for translation
817
path = ResourceLoader::import_remap(path); //remap for import
818
if (!path.is_resource_file()) {
819
return;
820
}
821
822
load(path);
823
}
824
825
void CompressedTextureLayered::_bind_methods() {
826
ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load);
827
ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path);
828
829
ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
830
}
831
832
CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {
833
layered_type = p_type;
834
}
835
836
CompressedTextureLayered::~CompressedTextureLayered() {
837
if (texture.is_valid()) {
838
ERR_FAIL_NULL(RenderingServer::get_singleton());
839
RS::get_singleton()->free_rid(texture);
840
}
841
}
842
843
/////////////////////////////////////////////////
844
845
Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
846
Ref<CompressedTextureLayered> ct;
847
if (p_path.has_extension("ctexarray")) {
848
Ref<CompressedTexture2DArray> c;
849
c.instantiate();
850
ct = c;
851
} else if (p_path.has_extension("ccube")) {
852
Ref<CompressedCubemap> c;
853
c.instantiate();
854
ct = c;
855
} else if (p_path.has_extension("ccubearray")) {
856
Ref<CompressedCubemapArray> c;
857
c.instantiate();
858
ct = c;
859
} else {
860
if (r_error) {
861
*r_error = ERR_FILE_UNRECOGNIZED;
862
}
863
return Ref<Resource>();
864
}
865
Error err = ct->load(p_path);
866
if (r_error) {
867
*r_error = err;
868
}
869
if (err != OK) {
870
return Ref<Resource>();
871
}
872
873
return ct;
874
}
875
876
void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
877
p_extensions->push_back("ctexarray");
878
p_extensions->push_back("ccube");
879
p_extensions->push_back("ccubearray");
880
}
881
882
bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const {
883
return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray";
884
}
885
886
String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const {
887
if (p_path.has_extension("ctexarray")) {
888
return "CompressedTexture2DArray";
889
}
890
if (p_path.has_extension("ccube")) {
891
return "CompressedCubemap";
892
}
893
if (p_path.has_extension("ccubearray")) {
894
return "CompressedCubemapArray";
895
}
896
return "";
897
}
898
899