Panda3D
eggTextureCards.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 eggTextureCards.cxx
10  * @author drose
11  * @date 2001-02-21
12  */
13 
14 #include "eggTextureCards.h"
15 
16 #include "eggGroup.h"
17 #include "eggVertexPool.h"
18 #include "eggVertex.h"
19 #include "eggTexture.h"
20 #include "eggPolygon.h"
21 #include "pnmImageHeader.h"
22 
23 #include <algorithm>
24 
25 using std::string;
26 
27 /**
28  *
29  */
30 EggTextureCards::
31 EggTextureCards() : EggWriter(true, true) {
32  set_program_brief("generate an .egg file containing texture cards");
33  set_program_description
34  ("egg-texture-cards generates an egg file consisting of several "
35  "square polygons, one for each texture name that appears on the "
36  "command line.\n\n"
37 
38  "This is a handy thing to have for importing texture images through "
39  "egg-palettize, even when those textures do not appear on any real "
40  "geometry; it can also be used for creating a lot of simple polygons "
41  "for rendering click buttons and similar interfaces.");
42 
43  clear_runlines();
44  add_runline("[opts] texture [texture ...] output.egg");
45  add_runline("[opts] -o output.egg texture [texture ...]");
46  add_runline("[opts] texture [texture ...] >output.egg");
47 
48  add_option
49  ("g", "left,right,bottom,top", 0,
50  "Specifies the geometry of each polygon. The default is a unit polygon "
51  "centered on the origin: -0.5,0.5,-0.5,0.5. Polygons are always created "
52  "on the X-Y plane. If -p is not also specified, all polygons will be "
53  "the same size and shape.",
54  &EggTextureCards::dispatch_double_quad, nullptr, &_polygon_geometry[0]);
55 
56  add_option
57  ("p", "xpixels,ypixels", 0,
58  "Indicates that polygons should be sized in proportion to the pixel "
59  "size of the texture image. This will potentially create a "
60  "different size and shape polygon for each texture. The coordinate "
61  "pair represents the image size in "
62  "pixels that will exactly fill up the polygon described with -g (or the "
63  "default polygon if -g is not specified); smaller images will be "
64  "given proportionately smaller polygons, and larger images will be "
65  "given proportionately larger polygons.",
66  &EggTextureCards::dispatch_double_pair, &_got_pixel_scale, &_pixel_scale[0]);
67 
68  add_option
69  ("suffix", "string", 0,
70  "Normally, each polygon is given a name based on the basename of its "
71  "corresponding texture's filename (without the filename extension). "
72  "This option specifies an ignorable suffix in the texture filename(s); "
73  "if this suffix is present, it is not included in the polygon's name. "
74  "This option may be repeated multiple times.",
75  &EggTextureCards::dispatch_vector_string, nullptr, &_suffixes);
76 
77  add_option
78  ("c", "r,g,b[,a]", 0,
79  "Specifies the color of each polygon. The default is white: 1,1,1,1.",
80  &EggTextureCards::dispatch_color, nullptr, &_polygon_color[0]);
81 
82  add_option
83  ("wm", "wrap", 0,
84  "Indicates the wrap mode of the texture: \"repeat\", \"clamp\", "
85  "or any of the other modes supported by egg syntax. "
86  "The default is to leave this unspecified.",
87  &EggTextureCards::dispatch_wrap_mode, nullptr, &_wrap_mode);
88 
89  add_option
90  ("wmu", "wrap_u", 0,
91  "Indicates the wrap mode of the texture in the U direction. This "
92  "overrides -wm, if specified.",
93  &EggTextureCards::dispatch_wrap_mode, nullptr, &_wrap_u);
94 
95  add_option
96  ("wmv", "wrap_v", 0,
97  "Indicates the wrap mode of the texture in the V direction. This "
98  "overrides -wm, if specified.",
99  &EggTextureCards::dispatch_wrap_mode, nullptr, &_wrap_v);
100 
101  add_option
102  ("minf", "filter", 0,
103  "Indicates the minfilter mode of the texture: \"linear\", \"mipmap\", "
104  "or any of the other modes supported by egg syntax. "
105  "The default is to leave this unspecified.",
106  &EggTextureCards::dispatch_filter_type, nullptr, &_minfilter);
107 
108  add_option
109  ("magf", "filter", 0,
110  "Indicates the magfilter mode of the texture: \"linear\" or \"nearest\". "
111  "The default is to leave this unspecified.",
112  &EggTextureCards::dispatch_filter_type, nullptr, &_magfilter);
113 
114  add_option
115  ("aniso", "degree", 0,
116  "Indicates the anisotropic degree of the texture. "
117  "The default is to leave this unspecified.",
118  &EggTextureCards::dispatch_int, &_got_aniso_degree, &_aniso_degree);
119 
120  add_option
121  ("ql", "[default | fastest | normal | best]", 0,
122  "Specifies the quality level of the texture. This mainly affects "
123  "the tinydisplay software renderer.",
124  &EggTextureCards::dispatch_quality_level, nullptr, &_quality_level);
125 
126  add_option
127  ("f", "format", 0,
128  "Indicates the format for all textures: typical choices are \"rgba12\" "
129  "or \"rgb5\" or \"alpha\". The default is to leave this unspecified.",
130  &EggTextureCards::dispatch_format, nullptr, &_format);
131 
132  add_option
133  ("f1", "format", 0,
134  "Indicates the format for one-channel textures only. If specified, this "
135  "overrides the format specified by -f.",
136  &EggTextureCards::dispatch_format, nullptr, &_format_1);
137 
138  add_option
139  ("f2", "format", 0,
140  "Indicates the format for two-channel textures only. If specified, this "
141  "overrides the format specified by -f.",
142  &EggTextureCards::dispatch_format, nullptr, &_format_2);
143 
144  add_option
145  ("f3", "format", 0,
146  "Indicates the format for three-channel textures only. If specified, this "
147  "overrides the format specified by -f.",
148  &EggTextureCards::dispatch_format, nullptr, &_format_3);
149 
150  add_option
151  ("f4", "format", 0,
152  "Indicates the format for four-channel textures only. If specified, this "
153  "overrides the format specified by -f.",
154  &EggTextureCards::dispatch_format, nullptr, &_format_4);
155 
156  add_option
157  ("b", "", 0,
158  "Make the textured polygons backfaced (two-sided).",
159  &EggTextureCards::dispatch_none, &_apply_bface);
160 
161  add_option
162  ("fps", "frame-rate", 0,
163  "Normally, all of the texture cards are created as a series of nodes "
164  "beneath a SequenceNode. This allows all of the cards to be viewed, "
165  "one at a time, if the output file is loaded in pview. It also has the "
166  "nice side-effect of creating an automatic texture flip that can be "
167  "used directly by applications; use this parameter to specify the "
168  "frame rate of that texture flip.",
169  &EggTextureCards::dispatch_double, nullptr, &_frame_rate);
170 
171  add_option
172  ("noexist", "", 0,
173  "Don't treat it as an error if the input file references pathnames "
174  "(e.g. textures) that don't exist. Normally, this will be flagged as "
175  "an error and the command aborted; with this option, an egg file will "
176  "be generated anyway, referencing pathnames that do not exist.",
177  &EggTextureCards::dispatch_none, &_noexist);
178 
179  _polygon_geometry.set(-0.5, 0.5, -0.5, 0.5);
180  _polygon_color.set(1.0, 1.0, 1.0, 1.0);
181  _wrap_mode = EggTexture::WM_unspecified;
182  _wrap_u = EggTexture::WM_unspecified;
183  _wrap_v = EggTexture::WM_unspecified;
184  _minfilter = EggTexture::FT_unspecified;
185  _magfilter = EggTexture::FT_unspecified;
186  _aniso_degree = 0;
187  _quality_level = EggTexture::QL_unspecified;
188  _format = EggTexture::F_unspecified;
189  _format_1 = EggTexture::F_unspecified;
190  _format_2 = EggTexture::F_unspecified;
191  _format_3 = EggTexture::F_unspecified;
192  _format_4 = EggTexture::F_unspecified;
193  _frame_rate = 2.0;
194 }
195 
196 /**
197  * Does something with the additional arguments on the command line (after all
198  * the -options have been parsed). Returns true if the arguments are good,
199  * false otherwise.
200  */
201 bool EggTextureCards::
202 handle_args(ProgramBase::Args &args) {
203  if (!check_last_arg(args, 0)) {
204  return false;
205  }
206 
207  if (args.empty()) {
208  nout << "No texture names specified on the command line.\n";
209  return false;
210  }
211 
212  ProgramBase::Args::iterator ai;
213  for (ai = args.begin(); ai != args.end(); ++ai) {
214  _texture_names.push_back(Filename::from_os_specific(*ai));
215  }
216 
217  return true;
218 }
219 
220 /**
221  * Standard dispatch function for an option that takes one parameter, which is
222  * to be interpreted as a WrapMode string. The data pointer is to a WrapMode
223  * enum variable.
224  */
225 bool EggTextureCards::
226 dispatch_wrap_mode(const string &opt, const string &arg, void *var) {
227  EggTexture::WrapMode *wmp = (EggTexture::WrapMode *)var;
228 
229  *wmp = EggTexture::string_wrap_mode(arg);
230  if (*wmp == EggTexture::WM_unspecified) {
231  // An unknown string. Let's check for our special cases.
232  if (arg == "r") {
233  *wmp = EggTexture::WM_repeat;
234  } else if (arg == "c") {
235  *wmp = EggTexture::WM_clamp;
236  } else {
237  nout << "Invalid wrap mode parameter for -" << opt << ": "
238  << arg << "\n";
239  return false;
240  }
241  }
242 
243  return true;
244 }
245 
246 /**
247  * Standard dispatch function for an option that takes one parameter, which is
248  * to be interpreted as a FilterType string. The data pointer is to a
249  * FilterType enum variable.
250  */
251 bool EggTextureCards::
252 dispatch_filter_type(const string &opt, const string &arg, void *var) {
253  EggTexture::FilterType *ftp = (EggTexture::FilterType *)var;
254 
255  *ftp = EggTexture::string_filter_type(arg);
256  if (*ftp == EggTexture::FT_unspecified) {
257  // An unknown string.
258  nout << "Invalid filter type parameter for -" << opt << ": "
259  << arg << "\n";
260  return false;
261  }
262 
263  return true;
264 }
265 
266 /**
267  * Standard dispatch function for an option that takes one parameter, which is
268  * to be interpreted as a QualityLevel string. The data pointer is to a
269  * QualityLevel enum variable.
270  */
271 bool EggTextureCards::
272 dispatch_quality_level(const string &opt, const string &arg, void *var) {
273  EggTexture::QualityLevel *qlp = (EggTexture::QualityLevel *)var;
274 
276  if (*qlp == EggTexture::QL_unspecified) {
277  nout << "Invalid quality level parameter for -" << opt << ": "
278  << arg << "\n";
279  return false;
280  }
281 
282  return true;
283 }
284 
285 /**
286  * Standard dispatch function for an option that takes one parameter, which is
287  * to be interpreted as a Format string. The data pointer is to a Format enum
288  * variable.
289  */
290 bool EggTextureCards::
291 dispatch_format(const string &opt, const string &arg, void *var) {
292  EggTexture::Format *fp = (EggTexture::Format *)var;
293 
294  *fp = EggTexture::string_format(arg);
295  if (*fp == EggTexture::F_unspecified) {
296  nout << "Invalid format parameter for -" << opt << ": "
297  << arg << "\n";
298  return false;
299  }
300 
301  return true;
302 }
303 
304 
305 /**
306  * Reads the texture image header to determine its size, and based on this
307  * size, computes the appropriate left,right,bottom,top geometry of the card
308  * that correspond to this texture.
309  *
310  * Returns true if successful, or false if the texture cannot be read.
311  */
312 bool EggTextureCards::
313 scan_texture(const Filename &filename, LVecBase4d &geometry,
314  int &num_channels) {
315  PNMImageHeader header;
316  if (!header.read_header(filename)) {
317  nout << "Unable to read image " << filename << "\n";
318  return false;
319  }
320 
321  num_channels = header.get_num_channels();
322 
323  double xscale = header.get_x_size() / _pixel_scale[0];
324  double yscale = header.get_y_size() / _pixel_scale[1];
325 
326  geometry.set(_polygon_geometry[0] * xscale,
327  _polygon_geometry[1] * xscale,
328  _polygon_geometry[2] * yscale,
329  _polygon_geometry[3] * yscale);
330  return true;
331 }
332 
333 /**
334  * Creates a set of four vertices for the polygon according to the
335  * left,right,bottom,top geometry.
336  */
337 void EggTextureCards::
338 make_vertices(const LPoint4d &geometry, EggVertexPool *vpool,
339  EggVertex *&v1, EggVertex *&v2, EggVertex *&v3, EggVertex *&v4) {
340  // 1 4 2 3
341 
342  v1 = vpool->make_new_vertex
343  (LPoint3d(geometry[0], geometry[3], 0.0));
344  v2 = vpool->make_new_vertex
345  (LPoint3d(geometry[0], geometry[2], 0.0));
346  v3 = vpool->make_new_vertex
347  (LPoint3d(geometry[1], geometry[2], 0.0));
348  v4 = vpool->make_new_vertex
349  (LPoint3d(geometry[1], geometry[3], 0.0));
350 
351  v1->set_uv(LTexCoordd(0.0, 1.0));
352  v2->set_uv(LTexCoordd(0.0, 0.0));
353  v3->set_uv(LTexCoordd(1.0, 0.0));
354  v4->set_uv(LTexCoordd(1.0, 1.0));
355 }
356 
357 /**
358  *
359  */
360 void EggTextureCards::
361 run() {
362  // First, create an enclosing group and a vertex pool with four vertices.
363  // We can use the same four vertices on all polygons.
364  bool all_ok = true;
365 
366  EggGroup *group = new EggGroup();
367  _data->add_child(group);
368 
369  // If we have more than one tile, make the group a sequence, as a
370  // convenience. If we view the egg file directly we can see all the tiles
371  // one at a time.
372  if (_texture_names.size() > 1) {
373  group->set_switch_flag(true);
374  group->set_switch_fps(_frame_rate);
375  }
376 
377  EggVertexPool *vpool = new EggVertexPool("vpool");
378  group->add_child(vpool);
379 
380  EggVertex *v1, *v2, *v3, *v4;
381 
382  if (!_got_pixel_scale) {
383  // If we don't have a per-texture pixel scale, all the polygons will be
384  // the same size, and hence may all share the same four vertices.
385  make_vertices(_polygon_geometry, vpool, v1, v2, v3, v4);
386  }
387 
388  // Now, create a texture reference and a polygon for each texture.
389 
390  vector_string::const_iterator ti;
391  for (ti = _texture_names.begin(); ti != _texture_names.end(); ++ti) {
392  Filename filename = (*ti);
393  string name = filename.get_basename_wo_extension();
394 
395  // Strip off any suffixes from the name.
396  vector_string::const_iterator si;
397  for (si = _suffixes.begin(); si != _suffixes.end(); ++si) {
398  const string &suffix = (*si);
399  int prefix = (int)name.length() - (int)suffix.length();
400  if (prefix > 0 && name.substr(prefix) == suffix) {
401  name = name.substr(0, prefix);
402  }
403  }
404 
405  // Read in the texture header and determine its size.
406  LVecBase4d geometry;
407  int num_channels;
408  bool texture_ok = scan_texture(filename, geometry, num_channels);
409  if (!texture_ok) {
410  all_ok = false;
411  }
412 
413  if (_got_pixel_scale) {
414  if (texture_ok) {
415  make_vertices(geometry, vpool, v1, v2, v3, v4);
416  } else {
417  make_vertices(_polygon_geometry, vpool, v1, v2, v3, v4);
418  }
419  }
420 
421  EggTexture *tref = new EggTexture(name, filename);
422  tref->set_wrap_mode(_wrap_mode);
423  tref->set_wrap_u(_wrap_u);
424  tref->set_wrap_v(_wrap_v);
425  tref->set_minfilter(_minfilter);
426  tref->set_magfilter(_magfilter);
427  if (_got_aniso_degree) {
428  tref->set_anisotropic_degree(_aniso_degree);
429  }
430  tref->set_quality_level(_quality_level);
431 
432  if (texture_ok) {
433  switch (num_channels) {
434  case 1:
435  tref->set_format(_format_1);
436  break;
437 
438  case 2:
439  tref->set_format(_format_2);
440  break;
441 
442  case 3:
443  tref->set_format(_format_3);
444  break;
445 
446  case 4:
447  tref->set_format(_format_4);
448  break;
449  }
450  }
451 
452  if (tref->get_format() == EggTexture::F_unspecified) {
453  tref->set_format(_format);
454  }
455 
456  group->add_child(tref);
457 
458  // Each polygon gets placed in its own sub-group. This will make pulling
459  // them out by name at runtime possible.
460  EggGroup *sub_group = new EggGroup(name);
461  group->add_child(sub_group);
462  EggPolygon *poly = new EggPolygon();
463  sub_group->add_child(poly);
464  poly->set_texture(tref);
465  poly->set_color(_polygon_color);
466  if (_apply_bface){
467  poly->set_bface_flag(1);
468  }
469 
470  poly->add_vertex(v1);
471  poly->add_vertex(v2);
472  poly->add_vertex(v3);
473  poly->add_vertex(v4);
474  }
475 
476  // Done!
477  if (all_ok || _noexist) {
478  write_egg_file();
479  } else {
480  nout << "Some textures not found; not generating egg file.\n";
481  exit(1);
482  }
483 }
484 
485 
486 int main(int argc, char *argv[]) {
487  EggTextureCards prog;
488  prog.parse_command_line(argc, argv);
489  prog.run();
490  return 0;
491 }
bool read_header(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Opens up the image file and tries to read its header information to determine its size,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_num_channels
Returns the number of channels in the image.
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
Definition: eggPrimitive.I:116
set_bface_flag
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.h:116
static QualityLevel string_quality_level(const std::string &string)
Returns the TexGen value associated with the given string representation, or ET_unspecified if the st...
static WrapMode string_wrap_mode(const std::string &string)
Returns the WrapMode value associated with the given string representation, or WM_unspecified if the ...
Definition: eggTexture.cxx:766
set_anisotropic_degree
Sets the degree of anisotropic filtering for this texture.
Definition: eggTexture.h:323
int get_y_size() const
Returns the number of pixels in the Y direction.
EggVertex * make_new_vertex()
Allocates and returns a new vertex from the pool.
Definition: eggVertexPool.I:37
int get_x_size() const
Returns the number of pixels in the X direction.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Format string_format(const std::string &string)
Returns the Format value associated with the given string representation, or F_unspecified if the str...
Definition: eggTexture.cxx:690
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write_egg_file()
Writes out the egg file as the normal result of the program.
Definition: eggWriter.cxx:177
A single polygon.
Definition: eggPolygon.h:24
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
Generates an egg file featuring a number of polygons, one for each named texture.
This is the base class for a program that generates an egg file output, but doesn't read any for inpu...
Definition: eggWriter.h:28
This is the base class of PNMImage, PNMReader, and PNMWriter.
static FilterType string_filter_type(const std::string &string)
Returns the FilterType value associated with the given string representation, or FT_unspecified if th...
Definition: eggTexture.cxx:788
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition: eggVertex.I:193
A collection of vertices.
Definition: eggVertexPool.h:41
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328