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