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  */
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  */
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  */
137 as_reader() {
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  */
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 }
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
static void convert_paths(EggNode *node, PathReplace *path_replace, const DSearchPath &additional_path)
Recursively walks the egg hierarchy.
Definition: eggBase.cxx:158
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
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
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
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
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
This is the base class for a program that reads egg files, but doesn't write an egg file.
Definition: eggReader.h:28
virtual void pre_process_egg_file()
Performs any processing of the egg file that is appropriate after reading it in.
Definition: eggReader.cxx:149
void add_texture_options()
Adds -td, -te, etc.
Definition: eggReader.cxx:70
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
void add_delod_options(double default_delod=-1.0)
Adds -delod as a valid option for this program.
Definition: eggReader.cxx:104
A SwitchCondition that switches the levels-of-detail based on distance from the camera's eyepoint.
This corresponds to a <SwitchCondition> entry within a group.
This is a collection of textures by TRef name.
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
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
void set_dirname(const std::string &s)
Replaces the directory part of the filename.
Definition: filename.cxx:704
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
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
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
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
bool write(const Filename &filename, PNMFileType *type=nullptr) const
Writes the image to the indicated filename.
Definition: pnmImage.cxx:385
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.