Panda3D
|
00001 // Filename: eggTextureCards.cxx 00002 // Created by: drose (21Feb01) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "eggTextureCards.h" 00016 00017 #include "eggGroup.h" 00018 #include "eggVertexPool.h" 00019 #include "eggVertex.h" 00020 #include "eggTexture.h" 00021 #include "eggPolygon.h" 00022 #include "pnmImageHeader.h" 00023 #include "pystub.h" 00024 00025 #include <algorithm> 00026 00027 //////////////////////////////////////////////////////////////////// 00028 // Function: EggTextureCards::Constructor 00029 // Access: Public 00030 // Description: 00031 //////////////////////////////////////////////////////////////////// 00032 EggTextureCards:: 00033 EggTextureCards() : EggWriter(true, true) { 00034 set_program_description 00035 ("egg-texture-cards generates an egg file consisting of several " 00036 "square polygons, one for each texture name that appears on the " 00037 "command line.\n\n" 00038 00039 "This is a handy thing to have for importing texture images through " 00040 "egg-palettize, even when those textures do not appear on any real " 00041 "geometry; it can also be used for creating a lot of simple polygons " 00042 "for rendering click buttons and similar interfaces."); 00043 00044 clear_runlines(); 00045 add_runline("[opts] texture [texture ...] output.egg"); 00046 add_runline("[opts] -o output.egg texture [texture ...]"); 00047 add_runline("[opts] texture [texture ...] >output.egg"); 00048 00049 add_option 00050 ("g", "left,right,bottom,top", 0, 00051 "Specifies the geometry of each polygon. The default is a unit polygon " 00052 "centered on the origin: -0.5,0.5,-0.5,0.5. Polygons are always created " 00053 "on the X-Y plane. If -p is not also specified, all polygons will be " 00054 "the same size and shape.", 00055 &EggTextureCards::dispatch_double_quad, NULL, &_polygon_geometry[0]); 00056 00057 add_option 00058 ("p", "xpixels,ypixels", 0, 00059 "Indicates that polygons should be sized in proportion to the pixel " 00060 "size of the texture image. This will potentially create a " 00061 "different size and shape polygon for each texture. The coordinate " 00062 "pair represents the image size in " 00063 "pixels that will exactly fill up the polygon described with -g (or the " 00064 "default polygon if -g is not specified); smaller images will be " 00065 "given proportionately smaller polygons, and larger images will be " 00066 "given proportionately larger polygons.", 00067 &EggTextureCards::dispatch_double_pair, &_got_pixel_scale, &_pixel_scale[0]); 00068 00069 add_option 00070 ("suffix", "string", 0, 00071 "Normally, each polygon is given a name based on the basename of its " 00072 "corresponding texture's filename (without the filename extension). " 00073 "This option specifies an ignorable suffix in the texture filename(s); " 00074 "if this suffix is present, it is not included in the polygon's name. " 00075 "This option may be repeated multiple times.", 00076 &EggTextureCards::dispatch_vector_string, NULL, &_suffixes); 00077 00078 add_option 00079 ("c", "r,g,b[,a]", 0, 00080 "Specifies the color of each polygon. The default is white: 1,1,1,1.", 00081 &EggTextureCards::dispatch_color, NULL, &_polygon_color[0]); 00082 00083 add_option 00084 ("wm", "wrap", 0, 00085 "Indicates the wrap mode of the texture: \"repeat\", \"clamp\", " 00086 "or any of the other modes supported by egg syntax. " 00087 "The default is to leave this unspecified.", 00088 &EggTextureCards::dispatch_wrap_mode, NULL, &_wrap_mode); 00089 00090 add_option 00091 ("wmu", "wrap_u", 0, 00092 "Indicates the wrap mode of the texture in the U direction. This " 00093 "overrides -wm, if specified.", 00094 &EggTextureCards::dispatch_wrap_mode, NULL, &_wrap_u); 00095 00096 add_option 00097 ("wmv", "wrap_v", 0, 00098 "Indicates the wrap mode of the texture in the V direction. This " 00099 "overrides -wm, if specified.", 00100 &EggTextureCards::dispatch_wrap_mode, NULL, &_wrap_v); 00101 00102 add_option 00103 ("minf", "filter", 0, 00104 "Indicates the minfilter mode of the texture: \"linear\", \"mipmap\", " 00105 "or any of the other modes supported by egg syntax. " 00106 "The default is to leave this unspecified.", 00107 &EggTextureCards::dispatch_filter_type, NULL, &_minfilter); 00108 00109 add_option 00110 ("magf", "filter", 0, 00111 "Indicates the magfilter mode of the texture: \"linear\" or \"nearest\". " 00112 "The default is to leave this unspecified.", 00113 &EggTextureCards::dispatch_filter_type, NULL, &_magfilter); 00114 00115 add_option 00116 ("aniso", "degree", 0, 00117 "Indicates the anisotropic degree of the texture. " 00118 "The default is to leave this unspecified.", 00119 &EggTextureCards::dispatch_int, &_got_aniso_degree, &_aniso_degree); 00120 00121 add_option 00122 ("ql", "[default | fastest | normal | best]", 0, 00123 "Specifies the quality level of the texture. This mainly affects " 00124 "the tinydisplay software renderer.", 00125 &EggTextureCards::dispatch_quality_level, NULL, &_quality_level); 00126 00127 add_option 00128 ("f", "format", 0, 00129 "Indicates the format for all textures: typical choices are \"rgba12\" " 00130 "or \"rgb5\" or \"alpha\". The default is to leave this unspecified.", 00131 &EggTextureCards::dispatch_format, NULL, &_format); 00132 00133 add_option 00134 ("f1", "format", 0, 00135 "Indicates the format for one-channel textures only. If specified, this " 00136 "overrides the format specified by -f.", 00137 &EggTextureCards::dispatch_format, NULL, &_format_1); 00138 00139 add_option 00140 ("f2", "format", 0, 00141 "Indicates the format for two-channel textures only. If specified, this " 00142 "overrides the format specified by -f.", 00143 &EggTextureCards::dispatch_format, NULL, &_format_2); 00144 00145 add_option 00146 ("f3", "format", 0, 00147 "Indicates the format for three-channel textures only. If specified, this " 00148 "overrides the format specified by -f.", 00149 &EggTextureCards::dispatch_format, NULL, &_format_3); 00150 00151 add_option 00152 ("f4", "format", 0, 00153 "Indicates the format for four-channel textures only. If specified, this " 00154 "overrides the format specified by -f.", 00155 &EggTextureCards::dispatch_format, NULL, &_format_4); 00156 00157 add_option 00158 ("b", "", 0, 00159 "Make the textured polygons backfaced (two-sided).", 00160 &EggTextureCards::dispatch_none, &_apply_bface); 00161 00162 add_option 00163 ("fps", "frame-rate", 0, 00164 "Normally, all of the texture cards are created as a series of nodes " 00165 "beneath a SequenceNode. This allows all of the cards to be viewed, " 00166 "one at a time, if the output file is loaded in pview. It also has the " 00167 "nice side-effect of creating an automatic texture flip that can be " 00168 "used directly by applications; use this parameter to specify the " 00169 "frame rate of that texture flip.", 00170 &EggTextureCards::dispatch_double, NULL, &_frame_rate); 00171 00172 add_option 00173 ("noexist", "", 0, 00174 "Don't treat it as an error if the input file references pathnames " 00175 "(e.g. textures) that don't exist. Normally, this will be flagged as " 00176 "an error and the command aborted; with this option, an egg file will " 00177 "be generated anyway, referencing pathnames that do not exist.", 00178 &EggTextureCards::dispatch_none, &_noexist); 00179 00180 _polygon_geometry.set(-0.5, 0.5, -0.5, 0.5); 00181 _polygon_color.set(1.0, 1.0, 1.0, 1.0); 00182 _wrap_mode = EggTexture::WM_unspecified; 00183 _wrap_u = EggTexture::WM_unspecified; 00184 _wrap_v = EggTexture::WM_unspecified; 00185 _minfilter = EggTexture::FT_unspecified; 00186 _magfilter = EggTexture::FT_unspecified; 00187 _aniso_degree = 0; 00188 _quality_level = EggTexture::QL_unspecified; 00189 _format = EggTexture::F_unspecified; 00190 _format_1 = EggTexture::F_unspecified; 00191 _format_2 = EggTexture::F_unspecified; 00192 _format_3 = EggTexture::F_unspecified; 00193 _format_4 = EggTexture::F_unspecified; 00194 _frame_rate = 2.0; 00195 } 00196 00197 //////////////////////////////////////////////////////////////////// 00198 // Function: EggTextureCards::handle_args 00199 // Access: Protected, Virtual 00200 // Description: Does something with the additional arguments on the 00201 // command line (after all the -options have been 00202 // parsed). Returns true if the arguments are good, 00203 // false otherwise. 00204 //////////////////////////////////////////////////////////////////// 00205 bool EggTextureCards:: 00206 handle_args(ProgramBase::Args &args) { 00207 if (!check_last_arg(args, 0)) { 00208 return false; 00209 } 00210 00211 if (args.empty()) { 00212 nout << "No texture names specified on the command line.\n"; 00213 return false; 00214 } 00215 00216 ProgramBase::Args::iterator ai; 00217 for (ai = args.begin(); ai != args.end(); ++ai) { 00218 _texture_names.push_back(Filename::from_os_specific(*ai)); 00219 } 00220 00221 return true; 00222 } 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: EggTextureCards::dispatch_wrap_mode 00226 // Access: Protected, Static 00227 // Description: Standard dispatch function for an option that takes 00228 // one parameter, which is to be interpreted as a 00229 // WrapMode string. The data pointer is to a WrapMode 00230 // enum variable. 00231 //////////////////////////////////////////////////////////////////// 00232 bool EggTextureCards:: 00233 dispatch_wrap_mode(const string &opt, const string &arg, void *var) { 00234 EggTexture::WrapMode *wmp = (EggTexture::WrapMode *)var; 00235 00236 *wmp = EggTexture::string_wrap_mode(arg); 00237 if (*wmp == EggTexture::WM_unspecified) { 00238 // An unknown string. Let's check for our special cases. 00239 if (arg == "r") { 00240 *wmp = EggTexture::WM_repeat; 00241 } else if (arg == "c") { 00242 *wmp = EggTexture::WM_clamp; 00243 } else { 00244 nout << "Invalid wrap mode parameter for -" << opt << ": " 00245 << arg << "\n"; 00246 return false; 00247 } 00248 } 00249 00250 return true; 00251 } 00252 00253 //////////////////////////////////////////////////////////////////// 00254 // Function: EggTextureCards::dispatch_filter_type 00255 // Access: Protected, Static 00256 // Description: Standard dispatch function for an option that takes 00257 // one parameter, which is to be interpreted as a 00258 // FilterType string. The data pointer is to a 00259 // FilterType enum variable. 00260 //////////////////////////////////////////////////////////////////// 00261 bool EggTextureCards:: 00262 dispatch_filter_type(const string &opt, const string &arg, void *var) { 00263 EggTexture::FilterType *ftp = (EggTexture::FilterType *)var; 00264 00265 *ftp = EggTexture::string_filter_type(arg); 00266 if (*ftp == EggTexture::FT_unspecified) { 00267 // An unknown string. 00268 nout << "Invalid filter type parameter for -" << opt << ": " 00269 << arg << "\n"; 00270 return false; 00271 } 00272 00273 return true; 00274 } 00275 00276 //////////////////////////////////////////////////////////////////// 00277 // Function: EggTextureCards::dispatch_quality_level 00278 // Access: Protected, Static 00279 // Description: Standard dispatch function for an option that takes 00280 // one parameter, which is to be interpreted as a 00281 // QualityLevel string. The data pointer is to a 00282 // QualityLevel enum variable. 00283 //////////////////////////////////////////////////////////////////// 00284 bool EggTextureCards:: 00285 dispatch_quality_level(const string &opt, const string &arg, void *var) { 00286 EggTexture::QualityLevel *qlp = (EggTexture::QualityLevel *)var; 00287 00288 *qlp = EggTexture::string_quality_level(arg); 00289 if (*qlp == EggTexture::QL_unspecified) { 00290 nout << "Invalid quality level parameter for -" << opt << ": " 00291 << arg << "\n"; 00292 return false; 00293 } 00294 00295 return true; 00296 } 00297 00298 //////////////////////////////////////////////////////////////////// 00299 // Function: EggTextureCards::dispatch_format 00300 // Access: Protected, Static 00301 // Description: Standard dispatch function for an option that takes 00302 // one parameter, which is to be interpreted as a 00303 // Format string. The data pointer is to a Format 00304 // enum variable. 00305 //////////////////////////////////////////////////////////////////// 00306 bool EggTextureCards:: 00307 dispatch_format(const string &opt, const string &arg, void *var) { 00308 EggTexture::Format *fp = (EggTexture::Format *)var; 00309 00310 *fp = EggTexture::string_format(arg); 00311 if (*fp == EggTexture::F_unspecified) { 00312 nout << "Invalid format parameter for -" << opt << ": " 00313 << arg << "\n"; 00314 return false; 00315 } 00316 00317 return true; 00318 } 00319 00320 00321 //////////////////////////////////////////////////////////////////// 00322 // Function: EggTextureCards::scan_texture 00323 // Access: Private 00324 // Description: Reads the texture image header to determine its size, 00325 // and based on this size, computes the appropriate 00326 // left,right,bottom,top geometry of the card that 00327 // correspond to this texture. 00328 // 00329 // Returns true if successful, or false if the texture 00330 // cannot be read. 00331 //////////////////////////////////////////////////////////////////// 00332 bool EggTextureCards:: 00333 scan_texture(const Filename &filename, LVecBase4d &geometry, 00334 int &num_channels) { 00335 PNMImageHeader header; 00336 if (!header.read_header(filename)) { 00337 nout << "Unable to read image " << filename << "\n"; 00338 return false; 00339 } 00340 00341 num_channels = header.get_num_channels(); 00342 00343 double xscale = header.get_x_size() / _pixel_scale[0]; 00344 double yscale = header.get_y_size() / _pixel_scale[1]; 00345 00346 geometry.set(_polygon_geometry[0] * xscale, 00347 _polygon_geometry[1] * xscale, 00348 _polygon_geometry[2] * yscale, 00349 _polygon_geometry[3] * yscale); 00350 return true; 00351 } 00352 00353 //////////////////////////////////////////////////////////////////// 00354 // Function: EggTextureCards::make_vertices 00355 // Access: Private 00356 // Description: Creates a set of four vertices for the polygon 00357 // according to the left,right,bottom,top geometry. 00358 //////////////////////////////////////////////////////////////////// 00359 void EggTextureCards:: 00360 make_vertices(const LPoint4d &geometry, EggVertexPool *vpool, 00361 EggVertex *&v1, EggVertex *&v2, EggVertex *&v3, EggVertex *&v4) { 00362 // 00363 // 1 4 00364 // 00365 // 00366 // 2 3 00367 // 00368 00369 v1 = vpool->make_new_vertex 00370 (LPoint3d(geometry[0], geometry[3], 0.0)); 00371 v2 = vpool->make_new_vertex 00372 (LPoint3d(geometry[0], geometry[2], 0.0)); 00373 v3 = vpool->make_new_vertex 00374 (LPoint3d(geometry[1], geometry[2], 0.0)); 00375 v4 = vpool->make_new_vertex 00376 (LPoint3d(geometry[1], geometry[3], 0.0)); 00377 00378 v1->set_uv(LTexCoordd(0.0, 1.0)); 00379 v2->set_uv(LTexCoordd(0.0, 0.0)); 00380 v3->set_uv(LTexCoordd(1.0, 0.0)); 00381 v4->set_uv(LTexCoordd(1.0, 1.0)); 00382 } 00383 00384 //////////////////////////////////////////////////////////////////// 00385 // Function: EggTextureCards::run 00386 // Access: Public 00387 // Description: 00388 //////////////////////////////////////////////////////////////////// 00389 void EggTextureCards:: 00390 run() { 00391 // First, create an enclosing group and a vertex pool with four 00392 // vertices. We can use the same four vertices on all polygons. 00393 bool all_ok = true; 00394 00395 EggGroup *group = new EggGroup(); 00396 _data->add_child(group); 00397 00398 // If we have more than one tile, make the group a sequence, as a 00399 // convenience. If we view the egg file directly we can see all the 00400 // tiles one at a time. 00401 if (_texture_names.size() > 1) { 00402 group->set_switch_flag(true); 00403 group->set_switch_fps(_frame_rate); 00404 } 00405 00406 EggVertexPool *vpool = new EggVertexPool("vpool"); 00407 group->add_child(vpool); 00408 00409 EggVertex *v1, *v2, *v3, *v4; 00410 00411 if (!_got_pixel_scale) { 00412 // If we don't have a per-texture pixel scale, all the polygons 00413 // will be the same size, and hence may all share the same four 00414 // vertices. 00415 make_vertices(_polygon_geometry, vpool, v1, v2, v3, v4); 00416 } 00417 00418 // Now, create a texture reference and a polygon for each texture. 00419 00420 vector_string::const_iterator ti; 00421 for (ti = _texture_names.begin(); ti != _texture_names.end(); ++ti) { 00422 Filename filename = (*ti); 00423 string name = filename.get_basename_wo_extension(); 00424 00425 // Strip off any suffixes from the name. 00426 vector_string::const_iterator si; 00427 for (si = _suffixes.begin(); si != _suffixes.end(); ++si) { 00428 const string &suffix = (*si); 00429 int prefix = (int)name.length() - (int)suffix.length(); 00430 if (prefix > 0 && name.substr(prefix) == suffix) { 00431 name = name.substr(0, prefix); 00432 } 00433 } 00434 00435 // Read in the texture header and determine its size. 00436 LVecBase4d geometry; 00437 int num_channels; 00438 bool texture_ok = scan_texture(filename, geometry, num_channels); 00439 if (!texture_ok) { 00440 all_ok = false; 00441 } 00442 00443 if (_got_pixel_scale) { 00444 if (texture_ok) { 00445 make_vertices(geometry, vpool, v1, v2, v3, v4); 00446 } else { 00447 make_vertices(_polygon_geometry, vpool, v1, v2, v3, v4); 00448 } 00449 } 00450 00451 EggTexture *tref = new EggTexture(name, filename); 00452 tref->set_wrap_mode(_wrap_mode); 00453 tref->set_wrap_u(_wrap_u); 00454 tref->set_wrap_v(_wrap_v); 00455 tref->set_minfilter(_minfilter); 00456 tref->set_magfilter(_magfilter); 00457 if (_got_aniso_degree) { 00458 tref->set_anisotropic_degree(_aniso_degree); 00459 } 00460 tref->set_quality_level(_quality_level); 00461 00462 if (texture_ok) { 00463 switch (num_channels) { 00464 case 1: 00465 tref->set_format(_format_1); 00466 break; 00467 00468 case 2: 00469 tref->set_format(_format_2); 00470 break; 00471 00472 case 3: 00473 tref->set_format(_format_3); 00474 break; 00475 00476 case 4: 00477 tref->set_format(_format_4); 00478 break; 00479 } 00480 } 00481 00482 if (tref->get_format() == EggTexture::F_unspecified) { 00483 tref->set_format(_format); 00484 } 00485 00486 group->add_child(tref); 00487 00488 // Each polygon gets placed in its own sub-group. This will make 00489 // pulling them out by name at runtime possible. 00490 EggGroup *sub_group = new EggGroup(name); 00491 group->add_child(sub_group); 00492 EggPolygon *poly = new EggPolygon(); 00493 sub_group->add_child(poly); 00494 poly->set_texture(tref); 00495 poly->set_color(_polygon_color); 00496 if (_apply_bface){ 00497 poly->set_bface_flag(1); 00498 } 00499 00500 poly->add_vertex(v1); 00501 poly->add_vertex(v2); 00502 poly->add_vertex(v3); 00503 poly->add_vertex(v4); 00504 } 00505 00506 // Done! 00507 if (all_ok || _noexist) { 00508 write_egg_file(); 00509 } else { 00510 nout << "Some textures not found; not generating egg file.\n"; 00511 exit(1); 00512 } 00513 } 00514 00515 00516 int main(int argc, char *argv[]) { 00517 // A call to pystub() to force libpystub.so to be linked in. 00518 pystub(); 00519 00520 EggTextureCards prog; 00521 prog.parse_command_line(argc, argv); 00522 prog.run(); 00523 return 0; 00524 }