Panda3D
textureReference.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file textureReference.cxx
10  * @author drose
11  * @date 2000-11-29
12  */
13 
14 #include "textureReference.h"
15 #include "textureImage.h"
16 #include "paletteImage.h"
17 #include "sourceTextureImage.h"
18 #include "destTextureImage.h"
19 #include "texturePlacement.h"
20 #include "palettizer.h"
21 #include "eggFile.h"
22 
23 #include "indent.h"
24 #include "eggTexture.h"
25 #include "eggData.h"
26 #include "eggGroupNode.h"
27 #include "eggGroup.h"
28 #include "eggNurbsSurface.h"
29 #include "eggVertexPool.h"
30 #include "datagram.h"
31 #include "datagramIterator.h"
32 #include "bamReader.h"
33 #include "bamWriter.h"
34 #include "string_utils.h"
35 
36 #include <math.h>
37 
38 using std::max;
39 using std::min;
40 using std::string;
41 
42 TypeHandle TextureReference::_type_handle;
43 
44 /**
45  *
46  */
47 TextureReference::
48 TextureReference() {
49  _egg_file = nullptr;
50  _egg_tex = nullptr;
51  _tex_mat = LMatrix3d::ident_mat();
52  _inv_tex_mat = LMatrix3d::ident_mat();
53  _source_texture = nullptr;
54  _placement = nullptr;
55  _uses_alpha = false;
56  _any_uvs = false;
57  _min_uv.set(0.0, 0.0);
58  _max_uv.set(0.0, 0.0);
59  _wrap_u = EggTexture::WM_unspecified;
60  _wrap_v = EggTexture::WM_unspecified;
61 }
62 
63 /**
64  *
65  */
66 TextureReference::
67 ~TextureReference() {
69 }
70 
71 /**
72  * Sets up the TextureReference using information extracted from an egg file.
73  */
75 from_egg(EggFile *egg_file, EggData *data, EggTexture *egg_tex) {
76  _egg_file = egg_file;
77  _egg_tex = egg_tex;
78  _egg_data = data;
79  _tref_name = egg_tex->get_name();
80 
81  if (_egg_tex->has_transform2d()) {
82  _tex_mat = _egg_tex->get_transform2d();
83  if (!_inv_tex_mat.invert_from(_tex_mat)) {
84  _inv_tex_mat = LMatrix3d::ident_mat();
85  }
86  } else {
87  _tex_mat = LMatrix3d::ident_mat();
88  _inv_tex_mat = LMatrix3d::ident_mat();
89  }
90 
91  Filename filename = _egg_tex->get_filename();
92  Filename alpha_filename;
93  if (_egg_tex->has_alpha_filename()) {
94  alpha_filename = _egg_tex->get_alpha_filename();
95  }
96  int alpha_file_channel = _egg_tex->get_alpha_file_channel();
97 
98  _properties._format = _egg_tex->get_format();
99  _properties._minfilter = _egg_tex->get_minfilter();
100  _properties._magfilter = _egg_tex->get_magfilter();
101  _properties._quality_level = _egg_tex->get_quality_level();
102  _properties._anisotropic_degree = _egg_tex->get_anisotropic_degree();
103 
104  string name = filename.get_basename_wo_extension();
105  TextureImage *texture = pal->get_texture(name);
106  if (texture->get_name() != name) {
107  nout << "Texture name conflict: \"" << name
108  << "\" conflicts with existing texture named \""
109  << texture->get_name() << "\".\n";
110 
111  // Make this a hard error; refuse to do anything else until the user fixes
112  // it. Case conflicts can be very bad, especially if CVS is involved on a
113  // Windows machine.
114  exit(1);
115  }
116  _source_texture = texture->get_source(filename, alpha_filename,
117  alpha_file_channel);
118  _source_texture->update_properties(_properties);
119 
120  _uses_alpha = false;
121  EggRenderMode::AlphaMode alpha_mode = _egg_tex->get_alpha_mode();
122  if (alpha_mode == EggRenderMode::AM_unspecified) {
123  if (_source_texture->get_size()) {
124  _uses_alpha =
125  _egg_tex->has_alpha_channel(_source_texture->get_num_channels());
126  }
127 
128  } else if (alpha_mode == EggRenderMode::AM_off) {
129  _uses_alpha = false;
130 
131  } else {
132  _uses_alpha = true;
133  }
134 
135  get_uv_range(_egg_data, pal->_remap_uv);
136 
137  _wrap_u = _egg_tex->determine_wrap_u();
138  _wrap_v = _egg_tex->determine_wrap_v();
139 }
140 
141 /**
142  * Sets up the pointers within the TextureReference to the same egg file
143  * pointers indicated by the other TextureReference object, without changing
144  * any of the other internal data stored here regarding the egg structures.
145  * This is intended for use when we have already shown that the two
146  * TextureReferences describe equivalent data.
147  */
150  nassertv(_tref_name == other._tref_name);
151  _egg_file = other._egg_file;
152  _egg_tex = other._egg_tex;
153  _egg_data = other._egg_data;
154 }
155 
156 /**
157  * Called to indicate that the EggData previously passed to from_egg() is
158  * about to be deallocated, and all of its pointers should be cleared.
159  */
162  _egg_tex = nullptr;
163  _egg_data = nullptr;
164 }
165 
166 /**
167  * After an EggData has previously been released via release_egg_data(), this
168  * can be called to indicate that the egg file has been reloaded and we should
169  * assign the indicated pointers.
170  */
173  nassertv(_tref_name == egg_tex->get_name());
174  _egg_data = data;
175  _egg_tex = egg_tex;
176 }
177 
178 /**
179  * Returns the EggFile that references this texture.
180  */
182 get_egg_file() const {
183  return _egg_file;
184 }
185 
186 /**
187  * Returns the SourceTextureImage that this object refers to.
188  */
190 get_source() const {
191  return _source_texture;
192 }
193 
194 /**
195  * Returns the TextureImage that this object refers to.
196  */
198 get_texture() const {
199  nassertr(_source_texture != nullptr, nullptr);
200  return _source_texture->get_texture();
201 }
202 
203 /**
204  * Returns the name of the EggTexture entry that references this texture.
205  */
206 const string &TextureReference::
207 get_tref_name() const {
208  return _tref_name;
209 }
210 
211 /**
212  * Defines an ordering of TextureReference pointers in alphabetical order by
213  * their tref name.
214  */
216 operator < (const TextureReference &other) const {
217  return _tref_name < other._tref_name;
218 }
219 
220 /**
221  * Returns true if this TextureReference actually uses the texture on
222  * geometry, with UV's and everything, or false otherwise. Strictly speaking,
223  * this should always return true.
224  */
226 has_uvs() const {
227  return _any_uvs;
228 }
229 
230 /**
231  * Returns the minimum UV coordinate in use for the texture by this reference.
232  */
233 const LTexCoordd &TextureReference::
234 get_min_uv() const {
235  nassertr(_any_uvs, _min_uv);
236  return _min_uv;
237 }
238 
239 /**
240  * Returns the maximum UV coordinate in use for the texture by this reference.
241  */
242 const LTexCoordd &TextureReference::
243 get_max_uv() const {
244  nassertr(_any_uvs, _max_uv);
245  return _max_uv;
246 }
247 
248 /**
249  * Returns the specification for the wrapping in the U direction.
250  */
251 EggTexture::WrapMode TextureReference::
252 get_wrap_u() const {
253  return _wrap_u;
254 }
255 
256 /**
257  * Returns the specification for the wrapping in the V direction.
258  */
259 EggTexture::WrapMode TextureReference::
260 get_wrap_v() const {
261  return _wrap_v;
262 }
263 
264 /**
265  * Returns true if all essential properties of this TextureReference are the
266  * same as that of the other, or false if any of them differ. This is useful
267  * when reading a new egg file and comparing its references to its previously-
268  * defined references.
269  */
271 is_equivalent(const TextureReference &other) const {
272  if (_source_texture != other._source_texture) {
273  return false;
274  }
275  if (!_properties.egg_properties_match(other._properties)) {
276  return false;
277  }
278  if (_uses_alpha != other._uses_alpha) {
279  return false;
280  }
281  if (_any_uvs != other._any_uvs) {
282  return false;
283  }
284  if (_wrap_u != other._wrap_u ||
285  _wrap_v != other._wrap_v) {
286  return false;
287  }
288  if (_any_uvs) {
289  if (!_min_uv.almost_equal(other._min_uv, 0.00001)) {
290  return false;
291  }
292  if (!_max_uv.almost_equal(other._max_uv, 0.00001)) {
293  return false;
294  }
295  }
296  if (!_tex_mat.almost_equal(other._tex_mat, 0.00001)) {
297  return false;
298  }
299 
300  return true;
301 }
302 
303 /**
304  * Sets the particular TexturePlacement that is appropriate for this egg file.
305  * This is called by EggFile::choose_placements().
306  */
309  if (_placement != placement) {
310  if (_placement != nullptr) {
311  // Remove our reference from the old placement object.
312  _placement->remove_egg(this);
313  }
314  _placement = placement;
315  if (_placement != nullptr) {
316  // Add our reference to the new placement object.
317  _placement->add_egg(this);
318  }
319  }
320 }
321 
322 /**
323  * Removes any reference to a TexturePlacement.
324  */
327  set_placement(nullptr);
328 }
329 
330 /**
331  * Returns the particular TexturePlacement that is appropriate for this egg
332  * file. This will not be filled in until EggFile::choose_placements() has
333  * been called.
334  */
336 get_placement() const {
337  return _placement;
338 }
339 
340 /**
341  * Marks the egg file that shares this reference as stale.
342  */
345  if (_egg_file != nullptr) {
346  _egg_file->mark_stale();
347  }
348 }
349 
350 /**
351  * Updates the egg file with all the relevant information to reference the
352  * texture in its new home, wherever that might be.
353  */
356  if (_egg_tex == nullptr) {
357  // Not much we can do if we don't have an actual egg file to reference.
358  return;
359  }
360 
361  if (_placement == nullptr) {
362  // Nor if we don't have an actual placement yet. This is possible if the
363  // egg was assigned to the "null" group, and the texture hasn't been re-
364  // assigned yet.
365  return;
366  }
367 
368  TextureImage *texture = get_texture();
369  if (texture != nullptr) {
370  // Make sure the alpha mode is set according to what the texture image
371  // wants.
372  if (texture->has_num_channels() &&
373  !_egg_tex->has_alpha_channel(texture->get_num_channels())) {
374  // The egg file doesn't want to use the alpha on the texture; leave it
375  // unspecified so the egg loader can figure out whether to enable alpha
376  // or not based on the object color.
377  _egg_tex->set_alpha_mode(EggRenderMode::AM_unspecified);
378 
379  } else {
380  // The egg file does want alpha, so get the alpha mode from the texture.
381  EggRenderMode::AlphaMode am = texture->get_alpha_mode();
382  if (am != EggRenderMode::AM_unspecified) {
383  _egg_tex->set_alpha_mode(am);
384  }
385  }
386 
387  // Also make sure the wrap mode is set properly.
388  if (texture->get_txa_wrap_u() != EggTexture::WM_unspecified) {
389  _egg_tex->set_wrap_u(texture->get_txa_wrap_u());
390  }
391  if (texture->get_txa_wrap_v() != EggTexture::WM_unspecified) {
392  _egg_tex->set_wrap_v(texture->get_txa_wrap_v());
393  }
394  }
395 
396  // We check for an OmitReason of OR_none, rather than asking is_placed(),
397  // because in this case we don't want to consider an OR_solitary texture as
398  // having been placed.
399  if (_placement->get_omit_reason() == OR_unknown) {
400  // The texture doesn't even exist. We can't update the egg to point to
401  // any meaningful path; just leave it pointing to the source texture's
402  // basename. Maybe it will be found along the texture path later.
403  Filename orig_filename = _egg_tex->get_filename();
404  texture->update_egg_tex(_egg_tex);
405  _egg_tex->set_filename(orig_filename.get_basename());
406  return;
407  }
408  if (_placement->get_omit_reason() != OR_none) {
409  // The texture exists but is not on a palette. This is the easy case; we
410  // simply have to update the texture reference to the new texture
411  // location.
412  DestTextureImage *dest = _placement->get_dest();
413  nassertv(dest != nullptr);
414  dest->update_egg_tex(_egg_tex);
415  return;
416  }
417 
418  // The texture *does* appear on a palette. This means we need to not only
419  // update the texture reference, but also adjust the UV's. In most cases,
420  // we can do this by simply applying a texture matrix to the reference.
421  PaletteImage *image = _placement->get_image();
422  nassertv(image != nullptr);
423 
424  image->update_egg_tex(_egg_tex);
425 
426  // Palette images never wrap, so the wrap mode doesn't matter. We let this
427  // default to unspecified, which means the images will wrap by default,
428  // which is the fastest mode for tinydisplay anyway.
429  _egg_tex->set_wrap_mode(EggTexture::WM_unspecified);
430  _egg_tex->set_wrap_u(EggTexture::WM_unspecified);
431  _egg_tex->set_wrap_v(EggTexture::WM_unspecified);
432 
433  LMatrix3d new_tex_mat;
434  _placement->compute_tex_matrix(new_tex_mat);
435 
436  // Compose the new texture matrix with whatever matrix was already there, if
437  // any.
438  _egg_tex->set_transform2d(_tex_mat * new_tex_mat);
439 
440  // Finally, go back and actually adjust the UV's to match what we claimed
441  // they could be.
442  if (_egg_tex->get_tex_gen() == EggTexture::TG_unspecified) {
443  update_uv_range(_egg_data, pal->_remap_uv);
444  }
445 }
446 
447 /**
448  * Applies the texture properties as read from the egg file to the source
449  * image's properties. This updates the source image with the now-known
450  * properties indicated with in the tref block of the egg file.
451  */
454  nassertv(_source_texture != nullptr);
455  _source_texture->update_properties(_properties);
456 }
457 
458 /**
459  *
460  */
461 void TextureReference::
462 output(std::ostream &out) const {
463  out << *_source_texture;
464 }
465 
466 /**
467  *
468  */
469 void TextureReference::
470 write(std::ostream &out, int indent_level) const {
471  indent(out, indent_level)
472  << get_texture()->get_name();
473 
474  if (_uses_alpha) {
475  out << " (uses alpha)";
476  }
477 
478  if (_any_uvs) {
479  // Compute the fraction of the image that is covered by the UV's minmax
480  // rectangle.
481  LTexCoordd box = _max_uv - _min_uv;
482  double area = box[0] * box[1];
483 
484  out << " coverage " << area;
485  }
486 
487  if (_wrap_u != EggTexture::WM_unspecified ||
488  _wrap_v != EggTexture::WM_unspecified) {
489  if (_wrap_u != _wrap_v) {
490  out << " (" << _wrap_u << ", " << _wrap_v << ")";
491  } else {
492  out << " " << _wrap_u;
493  }
494  }
495 
496  if (_properties._format != EggTexture::F_unspecified) {
497  out << " " << _properties._format;
498  }
499 
500  switch (_properties._minfilter) {
501  case EggTexture::FT_nearest_mipmap_nearest:
502  case EggTexture::FT_linear_mipmap_nearest:
503  case EggTexture::FT_nearest_mipmap_linear:
504  case EggTexture::FT_linear_mipmap_linear:
505  out << " mipmap";
506  break;
507 
508  default:
509  break;
510  }
511 
512  if(_properties._anisotropic_degree>1) {
513  out << " aniso " << _properties._anisotropic_degree;
514  }
515 
516  out << "\n";
517 }
518 
519 
520 /**
521  * Checks the geometry in the egg file to see what range of UV's are requested
522  * for this particular texture reference.
523  *
524  * If pal->_remap_uv is not RU_never, this will also attempt to remap the UV's
525  * found so that the midpoint lies in the unit square (0,0) - (1,1), in the
526  * hopes of maximizing overlap of UV coordinates between different polygons.
527  * However, the hypothetical translations are not actually applied to the egg
528  * file at this point (because we might decide not to place the texture in a
529  * palette); they will actually be applied when update_uv_range(), below, is
530  * called later.
531  *
532  * The return value is true if the search should continue, or false if it
533  * should abort prematurely.
534  */
535 bool TextureReference::
536 get_uv_range(EggGroupNode *group, Palettizer::RemapUV remap) {
537  if (group->is_of_type(EggGroup::get_class_type())) {
538  EggGroup *egg_group;
539  DCAST_INTO_R(egg_group, group, false);
540 
541  if (egg_group->get_dart_type() != EggGroup::DT_none) {
542  // If it's a character, we might change the kind of remapping we do.
543  remap = pal->_remap_char_uv;
544  }
545  }
546 
547  bool group_any_uvs = false;
548  LTexCoordd group_min_uv, group_max_uv;
549 
550  EggGroupNode::iterator ci;
551  for (ci = group->begin(); ci != group->end(); ci++) {
552  EggNode *child = (*ci);
553  if (child->is_of_type(EggNurbsSurface::get_class_type())) {
554  EggNurbsSurface *nurbs = DCAST(EggNurbsSurface, child);
555  if (nurbs->has_texture(_egg_tex)) {
556  // Here's a NURBS surface that references the texture. Unlike other
557  // kinds of geometries, NURBS don't store UV's; they're implicit in
558  // the surface. NURBS UV's will always run in the range (0, 0) - (1,
559  // 1). However, we do need to apply the texture matrix.
560 
561  // We also don't count the NURBS surfaces in with the group's UV's,
562  // because we can't adjust the UV's on a NURBS, so counting them up
563  // would be misleading (the reason we count up the group UV's is so we
564  // can consider adjusting them later). Instead, we just accumulate
565  // the NURBS UV's directly into our total.
566  collect_nominal_uv_range();
567  }
568 
569  } else if (child->is_of_type(EggPrimitive::get_class_type())) {
570  EggPrimitive *geom = DCAST(EggPrimitive, child);
571  if (geom->has_texture(_egg_tex)) {
572  // Here's a piece of geometry that references this texture. Walk
573  // through its vertices and get its UV's.
574 
575  if (_egg_tex->get_tex_gen() != EggTexture::TG_unspecified) {
576  // If the texture has a TexGen mode, we don't check the UV range on
577  // the model, since that doesn't matter. Instead, we assume the
578  // texture is used in the range (0, 0) - (1, 1), which will be true
579  // for a sphere map, although the effective range is a little less
580  // clear for the TG_world_position and similar modes.
581  collect_nominal_uv_range();
582 
583  // In fact, now we can return, having found at least one model that
584  // references the texture; there's no need to search further.
585  return false;
586 
587  } else {
588  LTexCoordd geom_min_uv, geom_max_uv;
589 
590  if (get_geom_uvs(geom, geom_min_uv, geom_max_uv)) {
591  if (remap == Palettizer::RU_poly) {
592  LVector2d trans = translate_uv(geom_min_uv, geom_max_uv);
593  geom_min_uv += trans;
594  geom_max_uv += trans;
595  }
596  collect_uv(group_any_uvs, group_min_uv, group_max_uv,
597  geom_min_uv, geom_max_uv);
598  }
599  }
600  }
601 
602  } else if (child->is_of_type(EggGroupNode::get_class_type())) {
603  EggGroupNode *cg = DCAST(EggGroupNode, child);
604  if (!get_uv_range(cg, remap)) {
605  return false;
606  }
607  }
608  }
609 
610  if (group_any_uvs) {
611  if (remap == Palettizer::RU_group) {
612  LVector2d trans = translate_uv(group_min_uv, group_max_uv);
613  group_min_uv += trans;
614  group_max_uv += trans;
615  }
616  collect_uv(_any_uvs, _min_uv, _max_uv, group_min_uv, group_max_uv);
617  }
618 
619  return true;
620 }
621 
622 /**
623  * Actually applies the UV translates that were assumed in the previous call
624  * to get_uv_range().
625  */
626 void TextureReference::
627 update_uv_range(EggGroupNode *group, Palettizer::RemapUV remap) {
628  if (group->is_of_type(EggGroup::get_class_type())) {
629  EggGroup *egg_group;
630  DCAST_INTO_V(egg_group, group);
631 
632  if (egg_group->get_dart_type() != EggGroup::DT_none) {
633  // If it's a character, we might change the kind of remapping we do.
634  remap = pal->_remap_char_uv;
635  }
636  }
637 
638  bool group_any_uvs = false;
639  LTexCoordd group_min_uv, group_max_uv;
640 
641  EggGroupNode::iterator ci;
642  for (ci = group->begin(); ci != group->end(); ci++) {
643  EggNode *child = (*ci);
644  if (child->is_of_type(EggNurbsSurface::get_class_type())) {
645  // We do nothing at this point for a Nurbs. Nothing we can do about
646  // these things.
647 
648  } else if (child->is_of_type(EggPrimitive::get_class_type())) {
649  if (remap != Palettizer::RU_never) {
650  EggPrimitive *geom = DCAST(EggPrimitive, child);
651  if (geom->has_texture(_egg_tex)) {
652  LTexCoordd geom_min_uv, geom_max_uv;
653 
654  if (get_geom_uvs(geom, geom_min_uv, geom_max_uv)) {
655  if (remap == Palettizer::RU_poly) {
656  LVector2d trans = translate_uv(geom_min_uv, geom_max_uv);
657  trans = trans * _inv_tex_mat;
658  if (!trans.almost_equal(LVector2d::zero())) {
659  translate_geom_uvs(geom, trans);
660  }
661  } else {
662  collect_uv(group_any_uvs, group_min_uv, group_max_uv,
663  geom_min_uv, geom_max_uv);
664  }
665  }
666  }
667  }
668 
669  } else if (child->is_of_type(EggGroupNode::get_class_type())) {
670  EggGroupNode *cg = DCAST(EggGroupNode, child);
671  update_uv_range(cg, remap);
672  }
673  }
674 
675  if (group_any_uvs && remap == Palettizer::RU_group) {
676  LVector2d trans = translate_uv(group_min_uv, group_max_uv);
677  trans = trans * _inv_tex_mat;
678  if (!trans.almost_equal(LVector2d::zero())) {
679  for (ci = group->begin(); ci != group->end(); ci++) {
680  EggNode *child = (*ci);
681  if (child->is_of_type(EggPrimitive::get_class_type())) {
682  EggPrimitive *geom = DCAST(EggPrimitive, child);
683  if (geom->has_texture(_egg_tex)) {
684  translate_geom_uvs(geom, trans);
685  }
686  }
687  }
688  }
689  }
690 }
691 
692 /**
693  * Determines the minimum and maximum UV range for a particular primitive.
694  * Returns true if it has any UV's, false otherwise.
695  */
696 bool TextureReference::
697 get_geom_uvs(EggPrimitive *geom,
698  LTexCoordd &geom_min_uv, LTexCoordd &geom_max_uv) {
699  string uv_name = _egg_tex->get_uv_name();
700  bool geom_any_uvs = false;
701 
702  EggPrimitive::iterator pi;
703  for (pi = geom->begin(); pi != geom->end(); ++pi) {
704  EggVertex *vtx = (*pi);
705  if (vtx->has_uv(uv_name)) {
706  LTexCoordd uv = vtx->get_uv(uv_name) * _tex_mat;
707  collect_uv(geom_any_uvs, geom_min_uv, geom_max_uv, uv, uv);
708  }
709  }
710 
711  return geom_any_uvs;
712 }
713 
714 /**
715  * Applies the indicated translation to each UV in the primitive.
716  */
717 void TextureReference::
718 translate_geom_uvs(EggPrimitive *geom, const LTexCoordd &trans) const {
719  string uv_name = _egg_tex->get_uv_name();
720 
721  EggPrimitive::iterator pi;
722  for (pi = geom->begin(); pi != geom->end(); ++pi) {
723  EggVertex *vtx = (*pi);
724  if (vtx->has_uv(uv_name)) {
725  EggVertex vtx_copy(*vtx);
726  vtx_copy.set_uv(uv_name, vtx_copy.get_uv(uv_name) + trans);
727  EggVertex *new_vtx = vtx->get_pool()->create_unique_vertex(vtx_copy);
728 
729  if (new_vtx->gref_size() != vtx->gref_size()) {
730  new_vtx->copy_grefs_from(*vtx);
731  }
732 
733  geom->replace(pi, new_vtx);
734  }
735  }
736 }
737 
738 /**
739  * Updates _any_uvs, _min_uv, and _max_uv with the range (0, 0) - (1, 1),
740  * adjusted by the texture matrix.
741  */
742 void TextureReference::
743 collect_nominal_uv_range() {
744  static const int num_nurbs_uvs = 4;
745  static LTexCoordd nurbs_uvs[num_nurbs_uvs] = {
746  LTexCoordd(0.0, 0.0),
747  LTexCoordd(0.0, 1.0),
748  LTexCoordd(1.0, 1.0),
749  LTexCoordd(1.0, 0.0)
750  };
751 
752  for (int i = 0; i < num_nurbs_uvs; i++) {
753  LTexCoordd uv = nurbs_uvs[i] * _tex_mat;
754  collect_uv(_any_uvs, _min_uv, _max_uv, uv, uv);
755  }
756 }
757 
758 /**
759  * Updates any_uvs, min_uv, and max_uv with the indicated min and max UV's
760  * already determined.
761  */
762 void TextureReference::
763 collect_uv(bool &any_uvs, LTexCoordd &min_uv, LTexCoordd &max_uv,
764  const LTexCoordd &got_min_uv, const LTexCoordd &got_max_uv) {
765  if (any_uvs) {
766  min_uv.set(min(min_uv[0], got_min_uv[0]),
767  min(min_uv[1], got_min_uv[1]));
768  max_uv.set(max(max_uv[0], got_max_uv[0]),
769  max(max_uv[1], got_max_uv[1]));
770  } else {
771  // The first UV.
772  min_uv = got_min_uv;
773  max_uv = got_max_uv;
774  any_uvs = true;
775  }
776 }
777 
778 /**
779  * Returns the needed adjustment to translate the given bounding box so that
780  * its center lies in the unit square (0,0) - (1,1).
781  */
782 LVector2d TextureReference::
783 translate_uv(const LTexCoordd &min_uv, const LTexCoordd &max_uv) {
784  LTexCoordd center = (min_uv + max_uv) / 2;
785  return LVector2d(-floor(center[0]), -floor(center[1]));
786 }
787 
788 /**
789  * Registers the current object as something that can be read from a Bam file.
790  */
794  register_factory(get_class_type(), make_TextureReference);
795 }
796 
797 /**
798  * Fills the indicated datagram up with a binary representation of the current
799  * object, in preparation for writing to a Bam file.
800  */
802 write_datagram(BamWriter *writer, Datagram &datagram) {
803  TypedWritable::write_datagram(writer, datagram);
804  writer->write_pointer(datagram, _egg_file);
805 
806  // We don't write _egg_tex or _egg_data; that's specific to the session.
807 
808  datagram.add_string(_tref_name);
809 
810  _tex_mat.write_datagram(datagram);
811  _inv_tex_mat.write_datagram(datagram);
812 
813  writer->write_pointer(datagram, _source_texture);
814  writer->write_pointer(datagram, _placement);
815 
816  datagram.add_bool(_uses_alpha);
817  datagram.add_bool(_any_uvs);
818  datagram.add_float64(_min_uv[0]);
819  datagram.add_float64(_min_uv[1]);
820  datagram.add_float64(_max_uv[0]);
821  datagram.add_float64(_max_uv[1]);
822  datagram.add_int32((int)_wrap_u);
823  datagram.add_int32((int)_wrap_v);
824  _properties.write_datagram(writer, datagram);
825 }
826 
827 /**
828  * Called after the object is otherwise completely read from a Bam file, this
829  * function's job is to store the pointers that were retrieved from the Bam
830  * file for each pointer object written. The return value is the number of
831  * pointers processed from the list.
832  */
835  int pi = TypedWritable::complete_pointers(p_list, manager);
836 
837  if (p_list[pi] != nullptr) {
838  DCAST_INTO_R(_egg_file, p_list[pi], pi);
839  }
840  pi++;
841 
842  if (p_list[pi] != nullptr) {
843  DCAST_INTO_R(_source_texture, p_list[pi], pi);
844  }
845  pi++;
846 
847  if (p_list[pi] != nullptr) {
848  DCAST_INTO_R(_placement, p_list[pi], pi);
849  }
850  pi++;
851 
852  pi += _properties.complete_pointers(p_list + pi, manager);
853 
854  return pi;
855 }
856 
857 /**
858  * This method is called by the BamReader when an object of this type is
859  * encountered in a Bam file; it should allocate and return a new object with
860  * all the data read.
861  */
862 TypedWritable* TextureReference::
863 make_TextureReference(const FactoryParams &params) {
865  DatagramIterator scan;
866  BamReader *manager;
867 
868  parse_params(params, scan, manager);
869  me->fillin(scan, manager);
870  return me;
871 }
872 
873 /**
874  * Reads the binary data from the given datagram iterator, which was written
875  * by a previous call to write_datagram().
876  */
877 void TextureReference::
878 fillin(DatagramIterator &scan, BamReader *manager) {
879  TypedWritable::fillin(scan, manager);
880  manager->read_pointer(scan); // _egg_file
881 
882  if (Palettizer::_read_pi_version >= 11) {
883  _tref_name = scan.get_string();
884  }
885 
886  _tex_mat.read_datagram(scan);
887  _inv_tex_mat.read_datagram(scan);
888 
889  manager->read_pointer(scan); // _source_texture
890  manager->read_pointer(scan); // _placement
891 
892  _uses_alpha = scan.get_bool();
893  _any_uvs = scan.get_bool();
894  _min_uv[0] = scan.get_float64();
895  _min_uv[1] = scan.get_float64();
896  _max_uv[0] = scan.get_float64();
897  _max_uv[1] = scan.get_float64();
898  _wrap_u = (EggTexture::WrapMode)scan.get_int32();
899  _wrap_v = (EggTexture::WrapMode)scan.get_int32();
900  _properties.fillin(scan, manager);
901 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:47
bool is_equivalent(const TextureReference &other) const
Returns true if all essential properties of this TextureReference are the same as that of the other,...
void from_egg_quick(const TextureReference &other)
Sets up the pointers within the TextureReference to the same egg file pointers indicated by the other...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
Definition: eggVertex.cxx:724
AlphaMode get_alpha_mode() const
Returns the alpha mode that was set, or AM_unspecified if nothing was set.
Definition: eggRenderMode.I:98
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
get_alpha_file_channel
Returns the particular channel that has been specified for the alpha-file image, or 0 if no channel h...
Definition: eggTexture.h:346
LMatrix3d get_transform2d() const
Returns the overall transform as a 3x3 matrix.
Definition: eggTransform.I:198
This represents a texture filename as it has been resized and copied to the map directory (e....
void set_placement(TexturePlacement *placement)
Sets the particular TexturePlacement that is appropriate for this egg file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void update_egg()
Updates the egg file with all the relevant information to reference the texture in its new home,...
void update_egg_tex(EggTexture *egg_tex) const
Sets the indicated EggTexture to refer to this file.
Definition: imageFile.cxx:403
void apply_properties_to_source()
Applies the texture properties as read from the egg file to the source image's properties.
bool get_bool()
Extracts a boolean value.
void set_transform2d(const LMatrix3d &mat)
Sets the overall transform as a 3x3 matrix.
Definition: eggTransform.I:165
EggTexture::WrapMode get_wrap_u() const
Returns the specification for the wrapping in the U direction.
get_uv_name
Returns the texcoord name that has been specified for this texture, or the empty string if no texcoor...
Definition: eggTexture.h:337
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition: datagram.I:123
bool has_transform2d() const
Returns true if the transform is specified as a 2-d transform, e.g.
Definition: eggTransform.I:156
void clear_placement()
Removes any reference to a TexturePlacement.
TextureImage * get_texture(const std::string &name)
Returns the TextureImage with the given name.
Definition: palettizer.cxx:838
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition: eggVertex.I:19
This is the particular reference of a texture filename by an egg file.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
static void register_with_read_factory()
Registers the current object as something that can be read from a Bam file.
EggFile * get_egg_file() const
Returns the EggFile that references this texture.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
has_alpha_filename
Returns true if a separate file for the alpha component has been applied, false otherwise.
Definition: eggTexture.h:343
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Definition: eggVertex.I:179
SourceTextureImage * get_source(const Filename &filename, const Filename &alpha_filename, int alpha_file_channel)
Returns the SourceTextureImage corresponding to the given filename(s).
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
void mark_stale()
Marks this particular egg file as stale, meaning that something has changed, such as the location of ...
Definition: eggFile.cxx:304
PaletteImage * get_image() const
Returns the particular PaletteImage on which the texture has been placed.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
TextureImage * get_texture() const
Returns the particular texture that this image is one of the sources for.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function's job is to store...
const LTexCoordd & get_min_uv() const
Returns the minimum UV coordinate in use for the texture by this reference.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
const LTexCoordd & get_max_uv() const
Returns the maximum UV coordinate in use for the texture by this reference.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
EggTexture::WrapMode get_txa_wrap_v() const
Returns the wrap mode specified in the v direction in the txa file, or WM_unspecified.
int32_t get_int32()
Extracts a signed 32-bit integer.
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggTexture::WrapMode get_wrap_v() const
Returns the specification for the wrapping in the V direction.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_string()
Extracts a variable-length string.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
SourceTextureImage * get_source() const
Returns the SourceTextureImage that this object refers to.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureImage * get_texture() const
Returns the TextureImage that this object refers to.
const std::string & get_tref_name() const
Returns the name of the EggTexture entry that references this texture.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
WrapMode determine_wrap_v() const
Determines the appropriate wrap in the V direction.
Definition: eggTexture.I:134
get_anisotropic_degree
Returns the anisotropic filtering degree that has been specified for this texture,...
Definition: eggTexture.h:323
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
void compute_tex_matrix(LMatrix3d &transform)
Stores in the indicated matrix the appropriate texture matrix transform for the new placement of the ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggRenderMode::AlphaMode get_alpha_mode() const
Returns the alpha mode that should be used to render objects with this texture, as specified by the u...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
void rebind_egg_data(EggData *data, EggTexture *egg_tex)
After an EggData has previously been released via release_egg_data(), this can be called to indicate ...
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
int get_num_channels() const
Returns the number of channels of the image.
Definition: imageFile.cxx:111
bool operator<(const TextureReference &other) const
Defines an ordering of TextureReference pointers in alphabetical order by their tref name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_alpha_mode(AlphaMode mode)
Specifies precisely how the transparency for this geometry should be achieved, or if it should be use...
Definition: eggRenderMode.I:89
bool has_uv() const
Returns true if the vertex has an unnamed UV coordinate pair, false otherwise.
Definition: eggVertex.I:158
bool get_size()
Determines the size of the SourceTextureImage, if it is not already known.
This corresponds to a particular assignment of a TextureImage with a PaletteGroup,...
void from_egg(EggFile *egg_file, EggData *data, EggTexture *egg_tex)
Sets up the TextureReference using information extracted from an egg file.
bool has_texture() const
Returns true if the primitive has any textures specified, false otherwise.
Definition: eggPrimitive.I:128
This is a texture image reference as it appears in an egg file: the source image of the texture.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_egg(TextureReference *reference)
Records the fact that a particular egg file is using this particular TexturePlacement.
OmitReason get_omit_reason() const
Returns the reason the texture has been omitted from a palette image, or OR_none if it has not.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
void fillin(DatagramIterator &scan, BamReader *manager)
Reads the binary data from the given datagram iterator, which was written by a previous call to write...
get_alpha_filename
Returns the separate file assigned for the alpha channel.
Definition: eggTexture.h:343
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
void mark_egg_stale()
Marks the egg file that shares this reference as stale.
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
void remove_egg(TextureReference *reference)
Notes that a particular egg file is no longer using this particular TexturePlacement.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
WrapMode determine_wrap_u() const
Determines the appropriate wrap in the U direction.
Definition: eggTexture.I:107
bool egg_properties_match(const TextureProperties &other) const
Returns true if all of the properties that are reflected directly in an egg file match between this T...
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
DestTextureImage * get_dest() const
Returns the DestTextureImage that corresponds to this texture as it was copied to the install directo...
A parametric NURBS surface.
bool has_num_channels() const
Returns true if the number of channels in the image is known, false otherwise.
Definition: imageFile.cxx:102
This is a single palette image, one of several within a PalettePage, which is in turn one of several ...
Definition: paletteImage.h:32
void replace(iterator position, EggVertex *vertex)
Replaces the vertex at the indicated position with the indicated vertex.
Definition: eggPrimitive.I:335
void update_properties(const TextureProperties &properties)
If the indicate TextureProperties structure is more specific than this one, updates this one.
Definition: imageFile.cxx:137
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
bool has_uvs() const
Returns true if this TextureReference actually uses the texture on geometry, with UV's and everything...
void copy_grefs_from(const EggVertex &other)
Copies all the group references from the other vertex onto this one.
Definition: eggVertex.cxx:747
A class to retrieve the individual data elements previously stored in a Datagram.
This represents a single source texture that is referenced by one or more egg files.
Definition: textureImage.h:46
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_alpha_channel(int num_components) const
Given the number of color components (channels) in the image file as actually read from the disk,...
Definition: eggTexture.cxx:501
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function's job is to store...
EggTexture::WrapMode get_txa_wrap_u() const
Returns the wrap mode specified in the u direction in the txa file, or WM_unspecified.
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
TexturePlacement * get_placement() const
Returns the particular TexturePlacement that is appropriate for this egg file.
void release_egg_data()
Called to indicate that the EggData previously passed to from_egg() is about to be deallocated,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This represents a single egg file known to the palettizer.
Definition: eggFile.h:36
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317