Panda3D
eggReader.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 eggReader.cxx
10  * @author drose
11  * @date 2000-02-14
12  */
13 
14 #include "eggReader.h"
15 
16 #include "pnmImage.h"
17 #include "config_putil.h"
18 #include "eggTextureCollection.h"
19 #include "eggGroup.h"
20 #include "eggGroupNode.h"
21 #include "eggSwitchCondition.h"
22 #include "string_utils.h"
23 #include "dcast.h"
24 
25 /**
26  *
27  */
28 EggReader::
29 EggReader() {
30  clear_runlines();
31  add_runline("[opts] input.egg");
32 
33  redescribe_option
34  ("cs",
35  "Specify the coordinate system to operate in. This may be "
36  " one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'. The default "
37  "is the coordinate system of the input egg file.");
38 
39  add_option
40  ("f", "", 80,
41  "Force complete loading: load up the egg file along with all of its "
42  "external references.",
43  &EggReader::dispatch_none, &_force_complete);
44 
45  add_option
46  ("noabs", "", 0,
47  "Don't allow the input egg file to have absolute pathnames. "
48  "If it does, abort with an error. This option is designed to help "
49  "detect errors when populating or building a standalone model tree, "
50  "which should be self-contained and include only relative pathnames.",
51  &EggReader::dispatch_none, &_noabs);
52 
53  _tex_type = nullptr;
54  _delod = -1.0;
55 
56  _got_tex_dirname = false;
57  _got_tex_extension = false;
58 }
59 
60 /**
61  * Adds -td, -te, etc. as valid options for this program. If the user
62  * specifies one of the options on the command line, the textures will be
63  * copied and converted as each egg file is read.
64  *
65  * Note that if you call this function to add these options, you must call
66  * do_reader_options() at the appropriate point before or during processing to
67  * execute the options if the user specified them.
68  */
69 void EggReader::
71  add_option
72  ("td", "dirname", 40,
73  "Copy textures to the indicated directory. The copy is performed "
74  "only if the destination file does not exist or is older than the "
75  "source file.",
76  &EggReader::dispatch_filename, &_got_tex_dirname, &_tex_dirname);
77 
78  add_option
79  ("te", "ext", 40,
80  "Rename textures to have the indicated extension. This also "
81  "automatically copies them to the new filename (possibly in a "
82  "different directory if -td is also specified), and may implicitly "
83  "convert to a different image format according to the extension.",
84  &EggReader::dispatch_string, &_got_tex_extension, &_tex_extension);
85 
86  add_option
87  ("tt", "type", 40,
88  "Explicitly specifies the image format to convert textures to "
89  "when copying them via -td or -te. Normally, this is unnecessary as "
90  "the image format can be determined by the extension, but sometimes "
91  "the extension is insufficient to unambiguously specify an image "
92  "type.",
93  &EggReader::dispatch_image_type, nullptr, &_tex_type);
94 }
95 
96 /**
97  * Adds -delod as a valid option for this program.
98  *
99  * Note that if you call this function to add these options, you must call
100  * do_reader_options() at the appropriate point before or during processing to
101  * execute the options if the user specified them.
102  */
103 void EggReader::
104 add_delod_options(double default_delod) {
105  _delod = default_delod;
106 
107  if (default_delod < 0) {
108  add_option
109  ("delod", "dist", 40,
110  "Eliminate LOD's by choosing the level that would be appropriate for "
111  "a camera at the indicated fixed distance from each LOD. "
112  "Use -delod -1 to keep all the LOD's as they are, which is "
113  "the default.\n",
114  &EggReader::dispatch_double, nullptr, &_delod);
115 
116  } else {
117  add_option
118  ("delod", "dist", 40,
119  "Eliminate LOD's by choosing the level that would be appropriate for "
120  "a camera at the indicated fixed distance from each LOD. "
121  "Use -delod -1 to keep all the LOD's as they are. The default value "
122  "is " + format_string(default_delod) + ".",
123  &EggReader::dispatch_double, nullptr, &_delod);
124  }
125 }
126 
127 /**
128  * Returns this object as an EggReader pointer, if it is in fact an EggReader,
129  * or NULL if it is not.
130  *
131  * This is intended to work around the C++ limitation that prevents downcasts
132  * past virtual inheritance. Since both EggReader and EggWriter inherit
133  * virtually from EggSingleBase, we need functions like this to downcast to
134  * the appropriate pointer.
135  */
138  return this;
139 }
140 
141 /**
142  * Performs any processing of the egg file that is appropriate after reading
143  * it in.
144  *
145  * Normally, you should not need to call this function directly; it is called
146  * automatically at startup.
147  */
148 void EggReader::
150 }
151 
152 /**
153  *
154  */
155 bool EggReader::
156 handle_args(ProgramBase::Args &args) {
157  if (args.empty()) {
158  nout << "You must specify the egg file(s) to read on the command line.\n";
159  return false;
160  }
161 
162  // Any separate egg files that are listed on the command line will get
163  // implicitly loaded up into one big egg file.
164 
165  if (!args.empty()) {
166  _data->set_egg_filename(Filename::from_os_specific(args[0]));
167  }
168  Args::const_iterator ai;
169  for (ai = args.begin(); ai != args.end(); ++ai) {
170  Filename filename = Filename::from_os_specific(*ai);
171 
172  EggData file_data;
173  if (filename != "-") {
174  if (!file_data.read(filename)) {
175  // Rather than returning false, we simply exit here, so the ProgramBase
176  // won't try to tell the user how to run the program just because we got
177  // a bad egg file.
178  exit(1);
179  }
180  } else {
181  if (!file_data.read(std::cin)) {
182  exit(1);
183  }
184  }
185 
186  if (_noabs && file_data.original_had_absolute_pathnames()) {
187  nout << filename.get_basename()
188  << " includes absolute pathnames!\n";
189  exit(1);
190  }
191 
192  DSearchPath file_path;
193  file_path.append_directory(filename.get_dirname());
194 
195  if (_force_complete) {
196  if (!file_data.load_externals(file_path)) {
197  exit(1);
198  }
199  }
200 
201  // Now resolve the filenames again according to the user's specified
202  // _path_replace.
203  convert_paths(&file_data, _path_replace, file_path);
204 
205  _data->merge(file_data);
206  }
207 
209 
210  return true;
211 }
212 
213 /**
214  * This is called after the command line has been completely processed, and it
215  * gives the program a chance to do some last-minute processing and validation
216  * of the options and arguments. It should return true if everything is fine,
217  * false if there is an error.
218  */
219 bool EggReader::
220 post_command_line() {
221  return EggSingleBase::post_command_line();
222 }
223 
224 /**
225  * Postprocesses the egg file as the user requested according to whatever
226  * command-line options are in effect. Returns true if everything is done
227  * correctly, false if there was some problem.
228  */
229 bool EggReader::
230 do_reader_options() {
231  bool okflag = true;
232 
233  if (_got_tex_dirname || _got_tex_extension) {
234  if (!copy_textures()) {
235  okflag = false;
236  }
237  }
238 
239  if (_delod >= 0.0) {
240  do_delod(_data);
241  }
242 
243  return okflag;
244 }
245 
246 /**
247  * Renames and copies the textures referenced in the egg file, if so specified
248  * by the -td and -te options. Returns true if all textures are copied
249  * successfully, false if any one of them failed.
250  */
251 bool EggReader::
252 copy_textures() {
253  bool success = true;
254  EggTextureCollection textures;
255  textures.find_used_textures(_data);
256 
257  EggTextureCollection::const_iterator ti;
258  for (ti = textures.begin(); ti != textures.end(); ++ti) {
259  EggTexture *tex = (*ti);
260  Filename orig_filename = tex->get_filename();
261  if (!orig_filename.exists()) {
262  bool found = orig_filename.resolve_filename(get_model_path());
263  if (!found) {
264  nout << "Cannot find " << orig_filename << "\n";
265  success = false;
266  continue;
267  }
268  }
269 
270  Filename new_filename = orig_filename;
271  if (_got_tex_dirname) {
272  new_filename.set_dirname(_tex_dirname);
273  }
274  if (_got_tex_extension) {
275  new_filename.set_extension(_tex_extension);
276  }
277 
278  if (orig_filename != new_filename) {
279  tex->set_filename(new_filename);
280 
281  // The new filename is different; does it need copying?
282  int compare =
283  orig_filename.compare_timestamps(new_filename, true, true);
284  if (compare > 0) {
285  // Yes, it does. Copy it!
286  nout << "Reading " << orig_filename << "\n";
287  PNMImage image;
288  if (!image.read(orig_filename)) {
289  nout << " unable to read!\n";
290  success = false;
291  } else {
292  nout << "Writing " << new_filename << "\n";
293  if (!image.write(new_filename, _tex_type)) {
294  nout << " unable to write!\n";
295  success = false;
296  }
297  }
298  }
299  }
300  }
301 
302  return success;
303 }
304 
305 /**
306  * Removes all the LOD's in the egg file by treating the camera as being
307  * _delod distance from each LOD. Returns true if this particular group should
308  * be preserved, false if it should be removed.
309  */
310 bool EggReader::
311 do_delod(EggNode *node) {
312  if (node->is_of_type(EggGroup::get_class_type())) {
313  EggGroup *group = DCAST(EggGroup, node);
314  if (group->has_lod()) {
315  const EggSwitchCondition &cond = group->get_lod();
316  if (cond.is_of_type(EggSwitchConditionDistance::get_class_type())) {
317  const EggSwitchConditionDistance *dist =
318  DCAST(EggSwitchConditionDistance, &cond);
319  if (_delod >= dist->_switch_out && _delod < dist->_switch_in) {
320  // Preserve this group node, but not the LOD information itself.
321  nout << "Preserving LOD " << node->get_name()
322  << " (" << dist->_switch_out << " to " << dist->_switch_in
323  << ")\n";
324  group->clear_lod();
325  } else {
326  // Remove this group node.
327  nout << "Eliminating LOD " << node->get_name()
328  << " (" << dist->_switch_out << " to " << dist->_switch_in
329  << ")\n";
330  return false;
331  }
332  }
333  }
334  }
335 
336  // Now process all the children.
337  if (node->is_of_type(EggGroupNode::get_class_type())) {
338  EggGroupNode *group = DCAST(EggGroupNode, node);
339  EggGroupNode::iterator ci;
340  ci = group->begin();
341  while (ci != group->end()) {
342  EggNode *child = *ci;
343  ++ci;
344 
345  if (!do_delod(child)) {
346  group->remove_child(child);
347  }
348  }
349  }
350 
351  return true;
352 }
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_delod_options(double default_delod=-1.0)
Adds -delod as a valid option for this program.
Definition: eggReader.cxx:104
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_texture_options()
Adds -td, -te, etc.
Definition: eggReader.cxx:70
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
bool write(const Filename &filename, PNMFileType *type=nullptr) const
Writes the image to the indicated filename.
Definition: pnmImage.cxx:385
virtual EggReader * as_reader()
Returns this object as an EggReader pointer, if it is in fact an EggReader, or NULL if it is not.
Definition: eggReader.cxx:137
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
bool resolve_filename(const DSearchPath &searchpath, const std::string &default_extension=std::string())
Searches the given search path for the filename.
Definition: filename.cxx:1581
This is a collection of textures by TRef name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void convert_paths(EggNode *node, PathReplace *path_replace, const DSearchPath &additional_path)
Recursively walks the egg hierarchy.
Definition: eggBase.cxx:158
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool read(Filename filename, std::string display_name=std::string())
Opens the indicated filename and reads the egg data contents from it.
Definition: eggData.cxx:65
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
bool load_externals(const DSearchPath &searchpath=DSearchPath())
Loads up all the egg files referenced by <File> entries within the egg structure, and inserts their c...
Definition: eggData.cxx:162
virtual void pre_process_egg_file()
Performs any processing of the egg file that is appropriate after reading it in.
Definition: eggReader.cxx:149
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
This is the base class for a program that reads egg files, but doesn't write an egg file.
Definition: eggReader.h:28
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
bool original_had_absolute_pathnames() const
Returns true if the data processed in the last call to read() contained absolute pathnames,...
Definition: eggData.I:82
int compare_timestamps(const Filename &other, bool this_missing_is_old=true, bool other_missing_is_old=true) const
Returns a number less than zero if the file named by this object is older than the given file,...
Definition: filename.cxx:1417
void set_dirname(const std::string &s)
Replaces the directory part of the filename.
Definition: filename.cxx:704
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
This corresponds to a <SwitchCondition> entry within a group.
A SwitchCondition that switches the levels-of-detail based on distance from the camera's eyepoint.
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.