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