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  */
149 from_egg_quick(const TextureReference &other) {
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  */
172 rebind_egg_data(EggData *data, EggTexture *egg_tex) {
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  */
308 set_placement(TexturePlacement *placement) {
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  */
326 clear_placement() {
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  */
344 mark_egg_stale() {
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  */
355 update_egg() {
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  */
834 complete_pointers(TypedWritable **p_list, BamReader *manager) {
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
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
A class to retrieve the individual data elements previously stored in a Datagram.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
bool get_bool()
Extracts a boolean value.
std::string get_string()
Extracts a variable-length string.
int32_t get_int32()
Extracts a signed 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition: datagram.I:123
This represents a texture filename as it has been resized and copied to the map directory (e....
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
This represents a single egg file known to the palettizer.
Definition: eggFile.h:36
void mark_stale()
Marks this particular egg file as stale, meaning that something has changed, such as the location of ...
Definition: eggFile.cxx:304
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
A parametric NURBS surface.
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:49
bool has_texture() const
Returns true if the primitive has any textures specified, false otherwise.
Definition: eggPrimitive.I:128
void replace(iterator position, EggVertex *vertex)
Replaces the vertex at the indicated position with the indicated vertex.
Definition: eggPrimitive.I:335
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
AlphaMode get_alpha_mode() const
Returns the alpha mode that was set, or AM_unspecified if nothing was set.
Definition: eggRenderMode.I:98
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
WrapMode determine_wrap_u() const
Determines the appropriate wrap in the U direction.
Definition: eggTexture.I:107
get_uv_name
Returns the texcoord name that has been specified for this texture, or the empty string if no texcoor...
Definition: eggTexture.h:341
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:350
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
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:327
get_alpha_filename
Returns the separate file assigned for the alpha channel.
Definition: eggTexture.h:347
has_alpha_filename
Returns true if a separate file for the alpha component has been applied, false otherwise.
Definition: eggTexture.h:347
bool has_transform2d() const
Returns true if the transform is specified as a 2-d transform, e.g.
Definition: eggTransform.I:156
void set_transform2d(const LMatrix3d &mat)
Sets the overall transform as a 3x3 matrix.
Definition: eggTransform.I:165
LMatrix3d get_transform2d() const
Returns the overall transform as a 3x3 matrix.
Definition: eggTransform.I:198
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.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
bool has_uv() const
Returns true if the vertex has an unnamed UV coordinate pair, false otherwise.
Definition: eggVertex.I:158
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition: eggVertex.I:19
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Definition: eggVertex.I:179
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
Definition: eggVertex.cxx:724
void copy_grefs_from(const EggVertex &other)
Copies all the group references from the other vertex onto this one.
Definition: eggVertex.cxx:747
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
void update_egg_tex(EggTexture *egg_tex) const
Sets the indicated EggTexture to refer to this file.
Definition: imageFile.cxx:403
int get_num_channels() const
Returns the number of channels of the image.
Definition: imageFile.cxx:111
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 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
TextureImage * get_texture(const std::string &name)
Returns the TextureImage with the given name.
Definition: palettizer.cxx:838
This is a texture image reference as it appears in an egg file: the source image of the texture.
bool get_size()
Determines the size of the SourceTextureImage, if it is not already known.
TextureImage * get_texture() const
Returns the particular texture that this image is one of the sources for.
This represents a single source texture that is referenced by one or more egg files.
Definition: textureImage.h:46
EggTexture::WrapMode get_txa_wrap_v() const
Returns the wrap mode specified in the v direction in the txa file, or WM_unspecified.
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...
SourceTextureImage * get_source(const Filename &filename, const Filename &alpha_filename, int alpha_file_channel)
Returns the SourceTextureImage corresponding to the given filename(s).
EggTexture::WrapMode get_txa_wrap_u() const
Returns the wrap mode specified in the u direction in the txa file, or WM_unspecified.
This corresponds to a particular assignment of a TextureImage with a PaletteGroup,...
OmitReason get_omit_reason() const
Returns the reason the texture has been omitted from a palette image, or OR_none if it has not.
DestTextureImage * get_dest() const
Returns the DestTextureImage that corresponds to this texture as it was copied to the install directo...
void remove_egg(TextureReference *reference)
Notes that a particular egg file is no longer using this particular TexturePlacement.
void add_egg(TextureReference *reference)
Records the fact that a particular egg file is using this particular TexturePlacement.
PaletteImage * get_image() const
Returns the particular PaletteImage on which the texture has been placed.
void compute_tex_matrix(LMatrix3d &transform)
Stores in the indicated matrix the appropriate texture matrix transform for the new placement of the ...
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...
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...
void fillin(DatagramIterator &scan, BamReader *manager)
Reads the binary data from the given datagram iterator, which was written by a previous call to write...
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
This is the particular reference of a texture filename by an egg file.
bool has_uvs() const
Returns true if this TextureReference actually uses the texture on geometry, with UV's and everything...
const std::string & get_tref_name() const
Returns the name of the EggTexture entry that references this texture.
bool is_equivalent(const TextureReference &other) const
Returns true if all essential properties of this TextureReference are the same as that of the other,...
static void register_with_read_factory()
Registers the current object as something that can be read from a Bam file.
void apply_properties_to_source()
Applies the texture properties as read from the egg file to the source image's properties.
void update_egg()
Updates the egg file with all the relevant information to reference the texture in its new home,...
const LTexCoordd & get_max_uv() const
Returns the maximum UV coordinate in use for the texture by this reference.
void from_egg(EggFile *egg_file, EggData *data, EggTexture *egg_tex)
Sets up the TextureReference using information extracted from an egg file.
void clear_placement()
Removes any reference to a TexturePlacement.
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 ...
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_wrap_u() const
Returns the specification for the wrapping in the U direction.
void set_placement(TexturePlacement *placement)
Sets the particular TexturePlacement that is appropriate for this egg file.
bool operator<(const TextureReference &other) const
Defines an ordering of TextureReference pointers in alphabetical order by their tref name.
EggFile * get_egg_file() const
Returns the EggFile that references this texture.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
EggTexture::WrapMode get_wrap_v() const
Returns the specification for the wrapping in the V direction.
TexturePlacement * get_placement() const
Returns the particular TexturePlacement that is appropriate for this egg file.
const LTexCoordd & get_min_uv() const
Returns the minimum UV coordinate in use for the texture by this reference.
TextureImage * get_texture() const
Returns the TextureImage that this object refers to.
SourceTextureImage * get_source() const
Returns the SourceTextureImage that this object refers to.
void from_egg_quick(const TextureReference &other)
Sets up the pointers within the TextureReference to the same egg file pointers indicated by the other...
void release_egg_data()
Called to indicate that the EggData previously passed to from_egg() is about to be deallocated,...
void mark_egg_stale()
Marks the egg file that shares this reference as stale.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
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...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.