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