Panda3D
Loading...
Searching...
No Matches
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
25using std::string;
26
27/**
28 *
29 */
30EggTextureCards::
31EggTextureCards() : 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 */
201bool EggTextureCards::
202handle_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 */
225bool EggTextureCards::
226dispatch_wrap_mode(const string &opt, const string &arg, void *var) {
227 EggTexture::WrapMode *wmp = (EggTexture::WrapMode *)var;
228
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 */
251bool EggTextureCards::
252dispatch_filter_type(const string &opt, const string &arg, void *var) {
253 EggTexture::FilterType *ftp = (EggTexture::FilterType *)var;
254
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 */
271bool EggTextureCards::
272dispatch_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 */
290bool EggTextureCards::
291dispatch_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 */
312bool EggTextureCards::
313scan_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 */
337void EggTextureCards::
338make_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 */
360void EggTextureCards::
361run() {
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) {
479 } else {
480 nout << "Some textures not found; not generating egg file.\n";
481 exit(1);
482 }
483}
484
485
486int main(int argc, char *argv[]) {
487 EggTextureCards prog;
488 prog.parse_command_line(argc, argv);
489 prog.run();
490 return 0;
491}
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition eggGroup.h:34
A single polygon.
Definition eggPolygon.h:24
set_bface_flag
Sets the backfacing flag of the polygon.
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
Generates an egg file featuring a number of polygons, one for each named texture.
Defines a texture map that may be applied to geometry.
Definition eggTexture.h:30
static Format string_format(const std::string &string)
Returns the Format value associated with the given string representation, or F_unspecified if the str...
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...
set_anisotropic_degree
Sets the degree of anisotropic filtering for this texture.
Definition eggTexture.h:327
static FilterType string_filter_type(const std::string &string)
Returns the FilterType value associated with the given string representation, or FT_unspecified if th...
static WrapMode string_wrap_mode(const std::string &string)
Returns the WrapMode value associated with the given string representation, or WM_unspecified if the ...
A collection of vertices.
EggVertex * make_new_vertex()
Allocates and returns a new vertex from the pool.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition eggVertex.h:39
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition eggVertex.I:193
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
void write_egg_file()
Writes out the egg file as the normal result of the program.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition filename.I:386
This is the base class of PNMImage, PNMReader, and PNMWriter.
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,...
int get_x_size() const
Returns the number of pixels in the X direction.
get_num_channels
Returns the number of channels in the image.
int get_y_size() const
Returns the number of pixels in the Y direction.
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_...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.