00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "pandabase.h"
00020 #include "texture.h"
00021 #include "config_gobj.h"
00022 #include "texturePool.h"
00023 #include "textureContext.h"
00024 #include "datagram.h"
00025 #include "datagramIterator.h"
00026 #include "bamReader.h"
00027 #include "bamWriter.h"
00028 #include "string_utils.h"
00029
00030 #include <stddef.h>
00031
00032
00033 TypeHandle Texture::_type_handle;
00034
00035
00036
00037
00038
00039
00040 static int
00041 up_to_power_2(int value) {
00042 int x = 1;
00043 while (x < value) {
00044 x = (x << 1);
00045 }
00046 return x;
00047 }
00048
00049
00050
00051
00052
00053
00054 static int
00055 down_to_power_2(int value) {
00056 int x = 1;
00057 while ((x << 1) <= value) {
00058 x = (x << 1);
00059 }
00060 return x;
00061 }
00062
00063
00064
00065
00066
00067
00068 static void
00069 consider_rescale(PNMImage &pnmimage, const string &name) {
00070 int new_x_size = pnmimage.get_x_size();
00071 int new_y_size = pnmimage.get_y_size();
00072
00073 if (textures_down_power_2) {
00074 new_x_size = down_to_power_2(new_x_size);
00075 new_y_size = down_to_power_2(new_y_size);
00076 } else if (textures_up_power_2) {
00077 new_x_size = up_to_power_2(new_x_size);
00078 new_y_size = up_to_power_2(new_y_size);
00079 }
00080
00081 if (textures_down_square) {
00082 new_x_size = new_y_size = min(new_x_size, new_y_size);
00083 } else if (textures_up_square) {
00084 new_x_size = new_y_size = max(new_x_size, new_y_size);
00085 }
00086
00087 if (max_texture_dimension > 0) {
00088 new_x_size = min(new_x_size, max_texture_dimension);
00089 new_y_size = min(new_y_size, max_texture_dimension);
00090 }
00091
00092 if (pnmimage.get_x_size() != new_x_size ||
00093 pnmimage.get_y_size() != new_y_size) {
00094 gobj_cat.info()
00095 << "Automatically rescaling " << name << " from "
00096 << pnmimage.get_x_size() << " by " << pnmimage.get_y_size() << " to "
00097 << new_x_size << " by " << new_y_size << "\n";
00098
00099 PNMImage scaled(new_x_size, new_y_size, pnmimage.get_num_channels(),
00100 pnmimage.get_maxval(), pnmimage.get_type());
00101 scaled.quick_filter_from(pnmimage);
00102 pnmimage = scaled;
00103 }
00104 }
00105
00106
00107
00108
00109
00110
00111 static void
00112 consider_downgrade(PNMImage &pnmimage, int num_channels,
00113 const string &name) {
00114 if (num_channels != 0 && num_channels < pnmimage.get_num_channels()) {
00115
00116
00117 if (pnmimage.get_num_channels() == 3 && num_channels == 2) {
00118 return;
00119 }
00120
00121 gobj_cat.info()
00122 << "Downgrading " << name << " from " << pnmimage.get_num_channels()
00123 << " components to " << num_channels << ".\n";
00124 pnmimage.set_num_channels(num_channels);
00125 }
00126 }
00127
00128
00129
00130
00131
00132
00133 Texture::
00134 Texture() : ImageBuffer() {
00135 _magfilter = FT_nearest;
00136 _minfilter = FT_nearest;
00137 _wrapu = WM_repeat;
00138 _wrapv = WM_repeat;
00139 _anisotropic_degree = 1;
00140 _keep_ram_image = false;
00141 _pbuffer = new PixelBuffer;
00142
00143 _all_dirty_flags = 0;
00144 memset(&_border_color,0,sizeof(Colorf));
00145 }
00146
00147
00148
00149
00150
00151
00152
00153 Texture::
00154 Texture(int xsize, int ysize, int components, int component_width, PixelBuffer::Type type,
00155 PixelBuffer::Format format, bool bAllocateRAM) : ImageBuffer() {
00156 _magfilter = FT_nearest;
00157 _minfilter = FT_nearest;
00158 _wrapu = WM_repeat;
00159 _wrapv = WM_repeat;
00160 _anisotropic_degree = 1;
00161 _keep_ram_image = bAllocateRAM;
00162 _pbuffer = new PixelBuffer(xsize,ysize,components,component_width,type,format,bAllocateRAM);
00163
00164 _all_dirty_flags = 0;
00165 memset(&_border_color,0,sizeof(Colorf));
00166 }
00167
00168
00169
00170
00171
00172
00173 Texture::
00174 ~Texture() {
00175 unprepare();
00176 }
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186 bool Texture::
00187 read(const Filename &fullpath, int primary_file_num_channels) {
00188 PNMImage image;
00189
00190 if (!image.read(fullpath)) {
00191 gobj_cat.error()
00192 << "Texture::read() - couldn't read: " << fullpath << endl;
00193 return false;
00194 }
00195
00196 if (!has_name()) {
00197 set_name(fullpath.get_basename_wo_extension());
00198 }
00199 if (!has_filename()) {
00200 set_filename(fullpath);
00201 clear_alpha_filename();
00202 }
00203
00204 set_fullpath(fullpath);
00205 clear_alpha_fullpath();
00206
00207
00208 consider_rescale(image, get_name());
00209 consider_downgrade(image, primary_file_num_channels, get_name());
00210
00211 _primary_file_num_channels = image.get_num_channels();
00212 _alpha_file_channel = 0;
00213
00214 return load(image);
00215 }
00216
00217
00218
00219
00220
00221
00222
00223 bool Texture::
00224 read(const Filename &fullpath, const Filename &alpha_fullpath,
00225 int primary_file_num_channels, int alpha_file_channel) {
00226 PNMImage image;
00227 if (!image.read(fullpath)) {
00228 gobj_cat.error()
00229 << "Texture::read() - couldn't read: " << fullpath << endl;
00230 return false;
00231 }
00232
00233 PNMImage alpha_image;
00234 if (!alpha_image.read(alpha_fullpath)) {
00235 gobj_cat.error()
00236 << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
00237 return false;
00238 }
00239
00240 if (!has_name()) {
00241 set_name(fullpath.get_basename_wo_extension());
00242 }
00243 if (!has_filename()) {
00244 set_filename(fullpath);
00245 set_alpha_filename(alpha_fullpath);
00246 }
00247
00248 set_fullpath(fullpath);
00249 set_alpha_fullpath(alpha_fullpath);
00250
00251 consider_rescale(image, get_name());
00252
00253
00254
00255 if (image.get_x_size() != alpha_image.get_x_size() ||
00256 image.get_y_size() != alpha_image.get_y_size()) {
00257 gobj_cat.info()
00258 << "Automatically rescaling " << alpha_fullpath.get_basename()
00259 << " from " << alpha_image.get_x_size() << " by "
00260 << alpha_image.get_y_size() << " to " << image.get_x_size()
00261 << " by " << image.get_y_size() << "\n";
00262
00263 PNMImage scaled(image.get_x_size(), image.get_y_size(),
00264 alpha_image.get_num_channels(),
00265 alpha_image.get_maxval(), alpha_image.get_type());
00266 scaled.quick_filter_from(alpha_image);
00267 alpha_image = scaled;
00268 }
00269
00270 consider_downgrade(image, primary_file_num_channels, get_name());
00271
00272 _primary_file_num_channels = image.get_num_channels();
00273
00274
00275
00276 image.add_alpha();
00277
00278 if (alpha_file_channel == 4 ||
00279 (alpha_file_channel == 2 && alpha_image.get_num_channels() == 2)) {
00280
00281 for (int x = 0; x < image.get_x_size(); x++) {
00282 for (int y = 0; y < image.get_y_size(); y++) {
00283 image.set_alpha(x, y, alpha_image.get_alpha(x, y));
00284 }
00285 }
00286 _alpha_file_channel = alpha_image.get_num_channels();
00287
00288 } else if (alpha_file_channel >= 1 && alpha_file_channel <= 3 &&
00289 alpha_image.get_num_channels() >= 3) {
00290
00291 for (int x = 0; x < image.get_x_size(); x++) {
00292 for (int y = 0; y < image.get_y_size(); y++) {
00293 image.set_alpha(x, y, alpha_image.get_channel_val(x, y, alpha_file_channel - 1));
00294 }
00295 }
00296 _alpha_file_channel = alpha_file_channel;
00297
00298 } else {
00299
00300 for (int x = 0; x < image.get_x_size(); x++) {
00301 for (int y = 0; y < image.get_y_size(); y++) {
00302 image.set_alpha(x, y, alpha_image.get_gray(x, y));
00303 }
00304 }
00305 _alpha_file_channel = 0;
00306 }
00307
00308 return load(image);
00309 }
00310
00311
00312
00313
00314
00315
00316 bool Texture::
00317 write(const Filename &name) const {
00318 nassertr(has_ram_image(), false);
00319 PNMImage pnmimage;
00320 if (!_pbuffer->store(pnmimage)) {
00321 return false;
00322 }
00323
00324 if (!pnmimage.write(name)) {
00325 gobj_cat.error()
00326 << "Texture::write() - couldn't write: " << name << endl;
00327 return false;
00328 }
00329 return true;
00330 }
00331
00332
00333
00334
00335
00336
00337 void Texture::
00338 set_wrapu(Texture::WrapMode wrap) {
00339 if (_wrapu != wrap) {
00340 mark_dirty(DF_wrap);
00341 _wrapu = wrap;
00342 }
00343 }
00344
00345
00346
00347
00348
00349
00350 void Texture::
00351 set_wrapv(Texture::WrapMode wrap) {
00352 if (_wrapv != wrap) {
00353 mark_dirty(DF_wrap);
00354 _wrapv = wrap;
00355 }
00356 }
00357
00358
00359
00360
00361
00362
00363 void Texture::
00364 set_minfilter(Texture::FilterType filter) {
00365 if (_minfilter != filter) {
00366 if (is_mipmap(_minfilter) != is_mipmap(filter)) {
00367 mark_dirty(DF_filter | DF_mipmap);
00368 } else {
00369 mark_dirty(DF_filter);
00370 }
00371 _minfilter = filter;
00372 }
00373 }
00374
00375
00376
00377
00378
00379
00380 void Texture::
00381 set_magfilter(Texture::FilterType filter) {
00382 if (_magfilter != filter) {
00383 mark_dirty(DF_filter);
00384 _magfilter = filter;
00385 }
00386 }
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397 void Texture::
00398 set_anisotropic_degree(int anisotropic_degree) {
00399 if (_anisotropic_degree != anisotropic_degree) {
00400 mark_dirty(DF_filter);
00401 _anisotropic_degree = anisotropic_degree;
00402 }
00403 }
00404
00405
00406
00407
00408
00409
00410 void Texture::
00411 set_border_color(const Colorf &color) {
00412 memcpy(&_border_color,&color,sizeof(Colorf));
00413 }
00414
00415
00416
00417
00418
00419
00420 bool Texture::
00421 load(const PNMImage &pnmimage) {
00422 if (!_pbuffer->load(pnmimage))
00423 return false;
00424
00425 mark_dirty(DF_image);
00426
00427 return true;
00428 }
00429
00430
00431
00432
00433
00434
00435
00436 bool Texture::
00437 store(PNMImage &pnmimage) const {
00438 return _pbuffer->store( pnmimage );
00439 }
00440
00441
00442
00443
00444
00445
00446
00447 bool Texture::
00448 is_mipmap(FilterType type) {
00449 switch (type) {
00450 case FT_nearest_mipmap_nearest:
00451 case FT_linear_mipmap_nearest:
00452 case FT_nearest_mipmap_linear:
00453 case FT_linear_mipmap_linear:
00454 return true;
00455
00456 default:
00457 return false;
00458 }
00459 }
00460
00461
00462
00463
00464
00465
00466
00467
00468 TextureContext *Texture::
00469 prepare(GraphicsStateGuardianBase *gsg) {
00470 Contexts::const_iterator ci;
00471 ci = _contexts.find(gsg);
00472 if (ci != _contexts.end()) {
00473 return (*ci).second;
00474 }
00475
00476 TextureContext *tc = gsg->prepare_texture(this);
00477 _contexts[gsg] = tc;
00478
00479
00480
00481
00482
00483 _all_dirty_flags = 0;
00484
00485 if (!keep_texture_ram && !_keep_ram_image) {
00486
00487
00488
00489
00490 if (gobj_cat.is_debug()) {
00491 gobj_cat.debug()
00492 << "Dumping RAM for texture " << get_name() << "\n";
00493 }
00494 _pbuffer->_image.clear();
00495 }
00496
00497 return tc;
00498 }
00499
00500
00501
00502
00503
00504
00505
00506 void Texture::
00507 unprepare() {
00508
00509
00510
00511
00512 Contexts temp = _contexts;
00513
00514 Contexts::const_iterator ci;
00515 for (ci = temp.begin(); ci != temp.end(); ++ci) {
00516 GraphicsStateGuardianBase *gsg = (*ci).first;
00517 TextureContext *tc = (*ci).second;
00518 gsg->release_texture(tc);
00519 }
00520
00521
00522
00523 nassertv(_contexts.empty());
00524 }
00525
00526
00527
00528
00529
00530
00531
00532 void Texture::
00533 unprepare(GraphicsStateGuardianBase *gsg) {
00534 Contexts::iterator ci;
00535 ci = _contexts.find(gsg);
00536 if (ci != _contexts.end()) {
00537 TextureContext *tc = (*ci).second;
00538 gsg->release_texture(tc);
00539 }
00540 }
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551 void Texture::
00552 clear_gsg(GraphicsStateGuardianBase *gsg) {
00553 Contexts::iterator ci;
00554 ci = _contexts.find(gsg);
00555 if (ci != _contexts.end()) {
00556 _contexts.erase(ci);
00557 } else {
00558
00559
00560 nassertv(false);
00561 }
00562 }
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588 PixelBuffer *Texture::
00589 get_ram_image() {
00590 if (!has_ram_image() && has_filename()) {
00591
00592 gobj_cat.info()
00593 << "Reloading texture " << get_name() << "\n";
00594
00595 if (has_alpha_fullpath()) {
00596 read(get_fullpath(), get_alpha_fullpath());
00597 } else {
00598 read(get_fullpath());
00599 }
00600 }
00601
00602 if (has_ram_image()) {
00603 return _pbuffer;
00604 } else {
00605 return (PixelBuffer *)NULL;
00606 }
00607 }
00608
00609 void Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) {
00610 gsg->copy_texture(prepare(gsg), dr);
00611 }
00612
00613 void Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr,
00614 const RenderBuffer &rb) {
00615 gsg->copy_texture(prepare(gsg), dr, rb);
00616 }
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634 void Texture::
00635 mark_dirty(int flags_to_set) {
00636 if ((_all_dirty_flags & flags_to_set) == flags_to_set) {
00637
00638
00639 return;
00640 }
00641
00642
00643 Contexts::iterator ci;
00644 for (ci = _contexts.begin(); ci != _contexts.end(); ++ci) {
00645 (*ci).second->mark_dirty(flags_to_set);
00646 }
00647
00648 _all_dirty_flags |= flags_to_set;
00649 }
00650
00651
00652
00653
00654
00655
00656
00657
00658 Texture::WrapMode Texture::
00659 string_wrap_mode(const string &string) {
00660 if (cmp_nocase_uh(string, "repeat") == 0) {
00661 return WM_repeat;
00662 } else if (cmp_nocase_uh(string, "clamp") == 0) {
00663 return WM_clamp;
00664 } else if (cmp_nocase_uh(string, "mirror") == 0) {
00665 return WM_clamp;
00666 } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
00667 return WM_clamp;
00668 } else if (cmp_nocase_uh(string, "border_color") == 0) {
00669 return WM_border_color;
00670 } else {
00671 return WM_invalid;
00672 }
00673 }
00674
00675
00676
00677
00678
00679
00680
00681
00682 Texture::FilterType Texture::
00683 string_filter_type(const string &string) {
00684 if (cmp_nocase_uh(string, "nearest") == 0) {
00685 return FT_nearest;
00686 } else if (cmp_nocase_uh(string, "linear") == 0) {
00687 return FT_linear;
00688 } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
00689 return FT_nearest_mipmap_nearest;
00690 } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
00691 return FT_linear_mipmap_nearest;
00692 } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
00693 return FT_nearest_mipmap_linear;
00694 } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
00695 return FT_linear_mipmap_linear;
00696 } else if (cmp_nocase_uh(string, "mipmap") == 0) {
00697 return FT_linear_mipmap_linear;
00698
00699 } else {
00700 return FT_invalid;
00701 }
00702 }
00703
00704
00705
00706
00707
00708
00709 void Texture::
00710 register_with_read_factory() {
00711 BamReader::get_factory()->register_factory(get_class_type(), make_Texture);
00712 }
00713
00714
00715
00716
00717
00718
00719 TypedWritable* Texture::
00720 make_Texture(const FactoryParams ¶ms) {
00721
00722
00723
00724
00725
00726
00727 DatagramIterator scan;
00728 BamReader *manager;
00729
00730 parse_params(params, scan, manager);
00731
00732
00733 string name = scan.get_string();
00734 Filename filename = scan.get_string();
00735 Filename alpha_filename = scan.get_string();
00736
00737 int primary_file_num_channels = 0;
00738 int alpha_file_channel = 0;
00739
00740 if (manager->get_file_minor_ver() == 2) {
00741
00742
00743 primary_file_num_channels = scan.get_uint8();
00744
00745 } else if (manager->get_file_minor_ver() >= 3) {
00746 primary_file_num_channels = scan.get_uint8();
00747 alpha_file_channel = scan.get_uint8();
00748 }
00749
00750 PT(Texture) me;
00751
00752 if (filename.empty()) {
00753
00754
00755 gobj_cat.info()
00756 << "Cannot create texture '" << name << "' with no filename.\n";
00757
00758 } else {
00759
00760 if (alpha_filename.empty()) {
00761 me = TexturePool::load_texture(filename, primary_file_num_channels);
00762 } else {
00763 me = TexturePool::load_texture(filename, alpha_filename,
00764 primary_file_num_channels, alpha_file_channel);
00765 }
00766 }
00767
00768 if (me == (Texture *)NULL) {
00769
00770
00771
00772 PT(Texture) dummy = new Texture;
00773 dummy->fillin(scan, manager);
00774
00775 } else {
00776 me->set_name(name);
00777 me->fillin(scan, manager);
00778 }
00779 return me;
00780 }
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790 void Texture::
00791 fillin(DatagramIterator &scan, BamReader *manager) {
00792
00793
00794
00795
00796
00797
00798
00799
00800 _wrapu = (enum WrapMode) scan.get_uint8();
00801 _wrapv = (enum WrapMode) scan.get_uint8();
00802 _minfilter = (enum FilterType) scan.get_uint8();
00803 _magfilter = (enum FilterType) scan.get_uint8();
00804 _anisotropic_degree = scan.get_int16();
00805
00806 bool has_pbuffer = scan.get_bool();
00807 if (has_pbuffer) {
00808 PixelBuffer::Format format = (PixelBuffer::Format)scan.get_uint8();
00809 int num_channels = -1;
00810 num_channels = scan.get_uint8();
00811
00812 if (_pbuffer != (PixelBuffer *)NULL) {
00813 if (num_channels == _pbuffer->get_num_components()) {
00814
00815
00816 _pbuffer->set_format(format);
00817 }
00818 }
00819 }
00820 }
00821
00822
00823
00824
00825
00826
00827
00828 void Texture::
00829 write_datagram(BamWriter *manager, Datagram &me) {
00830
00831
00832 bool has_pbuffer = (_pbuffer != (PixelBuffer *)NULL);
00833
00834
00835 ImageBuffer::write_datagram(manager, me);
00836
00837
00838 me.add_uint8(_wrapu);
00839 me.add_uint8(_wrapv);
00840 me.add_uint8(_minfilter);
00841 me.add_uint8(_magfilter);
00842 me.add_int16(_anisotropic_degree);
00843
00844 me.add_bool(has_pbuffer);
00845 if (has_pbuffer) {
00846 me.add_uint8(_pbuffer->get_format());
00847 me.add_uint8(_pbuffer->get_num_components());
00848 }
00849 }
00850