Panda3D
eggData.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 eggData.cxx
10  * @author drose
11  * @date 1999-01-20
12  */
13 
14 #include "eggData.h"
15 #include "eggCoordinateSystem.h"
16 #include "eggTextureCollection.h"
17 #include "eggMaterialCollection.h"
18 #include "eggComment.h"
19 #include "eggPoolUniquifier.h"
20 #include "config_egg.h"
21 #include "config_putil.h"
22 #include "config_express.h"
23 #include "string_utils.h"
24 #include "dSearchPath.h"
25 #include "virtualFileSystem.h"
26 #include "lightMutexHolder.h"
27 #include "zStream.h"
28 
29 using std::istream;
30 using std::ostream;
31 
32 extern int eggyyparse();
33 #include "parserDefs.h"
34 #include "lexerDefs.h"
35 
36 TypeHandle EggData::_type_handle;
37 
38 /**
39  * Looks for the indicated filename, first along the indicated searchpath, and
40  * then along the model_path. If found, updates the filename to the full path
41  * and returns true; otherwise, returns false.
42  */
43 bool EggData::
44 resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) {
46 
47  if (egg_filename.is_fully_qualified() && vfs->exists(egg_filename)) {
48  return true;
49  }
50 
51  vfs->resolve_filename(egg_filename, searchpath, "egg") ||
52  vfs->resolve_filename(egg_filename, get_model_path(), "egg");
53 
54  return vfs->exists(egg_filename);
55 }
56 
57 /**
58  * Opens the indicated filename and reads the egg data contents from it.
59  * Returns true if the file was successfully opened and read, false if there
60  * were some errors, in which case the data may be partially read.
61  *
62  * error is the output stream to which to write error messages.
63  */
64 bool EggData::
65 read(Filename filename, std::string display_name) {
66  filename.set_text();
67  set_egg_filename(filename);
68 
69  if (display_name.empty()) {
70  display_name = filename;
71  }
72 
74 
75  PT(VirtualFile) vfile = vfs->get_file(filename);
76  if (vfile == nullptr) {
77  egg_cat.error() << "Could not find " << display_name << "\n";
78  return false;
79  }
80  set_egg_timestamp(vfile->get_timestamp());
81 
82  istream *file = vfile->open_read_file(true);
83  if (file == nullptr) {
84  egg_cat.error() << "Unable to open " << display_name << "\n";
85  return false;
86  }
87 
88  egg_cat.info()
89  << "Reading " << display_name << "\n";
90 
91  bool read_ok = read(*file);
92  vfile->close_read_file(file);
93  return read_ok;
94 }
95 
96 
97 /**
98  * Parses the egg syntax contained in the indicated input stream. Returns
99  * true if the stream was a completely valid egg file, false if there were
100  * some errors, in which case the data may be partially read.
101  *
102  * Before you call this routine, you should probably call set_egg_filename()
103  * to set the name of the egg file we're processing, if at all possible. If
104  * there is no such filename, you may set it to the empty string.
105  */
106 bool EggData::
107 read(istream &in) {
108  // First, dispense with any children we had previously. We will replace
109  // them with the new data.
110  clear();
111 
112  // Create a temporary EggData structure to read into. We initialize it with
113  // a copy of ourselves, so that it will get our _coordsys value, if the user
114  // set it.
115  PT(EggData) data = new EggData(*this);
116 
117  int error_count;
118  {
119  LightMutexHolder holder(egg_lock);
120  egg_init_parser(in, get_egg_filename(), data, data);
121  eggyyparse();
122  egg_cleanup_parser();
123  error_count = egg_error_count();
124  }
125 
126  data->post_read();
127 
128  steal_children(*data);
129  (*this) = *data;
130 
131  return (error_count == 0);
132 }
133 
134 /**
135  * Appends the other egg structure to the end of this one. The other egg
136  * structure is invalidated.
137  */
138 void EggData::
139 merge(EggData &other) {
140  if (get_coordinate_system() == CS_default) {
141  // If we haven't specified a coordinate system yet, we inherit the other
142  // one's.
144 
145  } else {
146  // Otherwise, the other one is forced into our coordinate system before we
147  // merge.
149  }
150  steal_children(other);
151 }
152 
153 
154 /**
155  * Loads up all the egg files referenced by <File> entries within the egg
156  * structure, and inserts their contents in place of the <File> entries.
157  * Searches for files in the searchpath, if not found directly, and writes
158  * error messages to the indicated output stream. Returns true if all
159  * externals were loaded successfully, false otherwise.
160  */
161 bool EggData::
162 load_externals(const DSearchPath &searchpath) {
163  return
164  r_load_externals(searchpath, get_coordinate_system(), nullptr);
165 }
166 
167 /**
168  * Loads up all the egg files referenced by <File> entries within the egg
169  * structure, and inserts their contents in place of the <File> entries.
170  * Searches for files in the searchpath, if not found directly, and writes
171  * error messages to the indicated output stream. Returns true if all
172  * externals were loaded successfully, false otherwise.
173  */
174 bool EggData::
175 load_externals(const DSearchPath &searchpath, BamCacheRecord *record) {
176  return
177  r_load_externals(searchpath, get_coordinate_system(), record);
178 }
179 
180 /**
181  * Removes duplicate references to the same texture image with the same
182  * properties. Considers two texture references with identical properties,
183  * but different tref names, to be equivalent, and collapses them, choosing
184  * one tref name to keep arbitrarily. Returns the number of textures removed.
185  */
186 int EggData::
188  EggTextureCollection textures;
189  textures.find_used_textures(this);
190  return
191  textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this);
192 }
193 
194 /**
195  * Removes duplicate references to the same material with the same properties.
196  * Considers two material references with identical properties, but different
197  * mref names, to be equivalent, and collapses them, choosing one mref name to
198  * keep arbitrarily. Returns the number of materials removed.
199  */
200 int EggData::
202  EggMaterialCollection materials;
203  materials.find_used_materials(this);
204  return
205  materials.collapse_equivalent_materials(~EggMaterial::E_mref_name, this);
206 }
207 
208 /**
209  * The main interface for writing complete egg files.
210  */
211 bool EggData::
212 write_egg(Filename filename) {
214  filename.set_text();
215  vfs->delete_file(filename);
216  ostream *file = vfs->open_write_file(filename, true, true);
217  if (file == nullptr) {
218  egg_cat.error() << "Unable to open " << filename << " for writing.\n";
219  return false;
220  }
221 
222  bool wrote_ok = write_egg(*file);
223  vfs->close_write_file(file);
224  return wrote_ok;
225 }
226 
227 /**
228  * The main interface for writing complete egg files.
229  */
230 bool EggData::
231 write_egg(ostream &out) {
232  pre_write();
233 
234  if (egg_precision > 0) {
235  // Change the egg precision as requested.
236  std::streamsize orig_precision = out.precision();
237  out.precision((std::streamsize)egg_precision);
238  write(out, 0);
239  out.precision(orig_precision);
240  } else {
241  // Use the stream default precision.
242  write(out, 0);
243  }
244  return true;
245 }
246 
247 
248 /**
249  * Changes the coordinate system of the EggData. If the coordinate system was
250  * previously different, this may result in a conversion of the data.
251  */
252 void EggData::
253 set_coordinate_system(CoordinateSystem new_coordsys) {
254  if (new_coordsys == CS_default) {
255  new_coordsys = get_default_coordinate_system();
256  }
257  if (new_coordsys != _coordsys &&
258  (_coordsys != CS_default && _coordsys != CS_invalid)) {
259  // Time to convert the data.
260  LMatrix4d mat = LMatrix4d::convert_mat(_coordsys, new_coordsys);
261  LMatrix4d inv = LMatrix4d::convert_mat(new_coordsys, _coordsys);
262 
263  r_transform(mat, inv, new_coordsys);
264  r_transform_vertices(mat);
265 
266  // Now we have to update the under_flags to ensure that all the cached
267  // relative matrices are correct.
268  update_under(0);
269  }
270 
271  _coordsys = new_coordsys;
272 }
273 
274 /**
275  * Writes the egg data out to the indicated output stream.
276  */
277 void EggData::
278 write(ostream &out, int indent_level) const {
279  PT(EggCoordinateSystem) ecs = new EggCoordinateSystem(_coordsys);
280  ecs->write(out, indent_level);
281  EggGroupNode::write(out, indent_level);
282  out << std::flush;
283 }
284 
285 
286 /**
287  * Does whatever processing is appropriate after reading the data in from an
288  * egg file.
289  */
290 void EggData::
291 post_read() {
292  CoordinateSystem old_coordsys = _coordsys;
293  _coordsys = find_coordsys_entry();
294 
295  if (_coordsys == CS_default) {
296  // If the egg file didn't contain a <CoordinateSystem> entry, assume it's
297  // Y-up, by convention.
298  _coordsys = CS_yup_right;
299 
300  } else if (_coordsys == CS_invalid) {
301  egg_cat.warning()
302  << "Contradictory <CoordinateSystem> entries encountered.\n";
303  _coordsys = CS_yup_right;
304  }
305 
306  r_mark_coordsys(_coordsys);
307 
308  if (old_coordsys != CS_default) {
309  // Now if we had a previous definition, enforce it. This might convert
310  // the data to the given coordinate system.
311  set_coordinate_system(old_coordsys);
312  }
313 
314  // Fill this in before we automatically resolve pathnames.
315  _had_absolute_pathnames = has_absolute_pathnames();
316 
318  // Resolve filenames that are relative to the egg file.
319  DSearchPath dir;
320  dir.append_directory(get_egg_filename().get_dirname());
321  resolve_filenames(dir);
322  }
323 }
324 
325 /**
326  * Does whatever processing is appropriate just before writing the data out to
327  * an egg file. This includes verifying that vertex pool names are unique,
328  * etc.
329  */
330 void EggData::
331 pre_write() {
332  // Pull out all of the texture definitions in the file and massage them a
333  // bit.
334  EggTextureCollection textures;
335  textures.extract_textures(this);
336 
337  // Remove any textures that aren't being used.
338  textures.remove_unused_textures(this);
339 
340  // Collapse out any textures that are completely equivalent. For this
341  // purpose, we consider two textures with identical properties but different
342  // tref names to be different.
343  textures.collapse_equivalent_textures(~0, this);
344 
345  // Make sure all of the textures have unique TRef names.
346  textures.uniquify_trefs();
347  textures.sort_by_tref();
348 
349  // Do the same thing with the materials.
350  EggMaterialCollection materials;
351  materials.extract_materials(this);
352  materials.remove_unused_materials(this);
353  materials.collapse_equivalent_materials(~0, this);
354  materials.uniquify_mrefs();
355  materials.sort_by_mref();
356 
357  // Now put them all back at the head of the file, after any initial comment
358  // records.
359  iterator ci = begin();
360  while (ci != end() && (*ci)->is_of_type(EggComment::get_class_type())) {
361  ++ci;
362  }
363  textures.insert_textures(this, ci);
364  materials.insert_materials(this, ci);
365 
366  // Also make sure that the vertex pools are uniquely named. This also
367  // checks textures and materials, which is kind of redundant since we just
368  // did that, but we don't mind.
370  pu.uniquify(this);
371 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void sort_by_mref()
Sorts all the materials into alphabetical order by MRef name.
void sort_by_tref()
Sorts all the textures into alphabetical order by TRef name.
int collapse_equivalent_textures()
Removes duplicate references to the same texture image with the same properties.
Definition: eggData.cxx:187
static bool resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath=DSearchPath())
Looks for the indicated filename, first along the indicated searchpath, and then along the model_path...
Definition: eggData.cxx:44
int collapse_equivalent_textures(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate textures that are equivalent accordi...
void uniquify_trefs()
Guarantees that each texture in the collection has a unique TRef name.
void uniquify_mrefs()
Guarantees that each material in the collection has a unique MRef name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The <CoordinateSystem> entry at the top of an egg file.
bool write_egg(Filename filename)
The main interface for writing complete egg files.
Definition: eggData.cxx:212
This is a collection of materials by MRef name.
A hierarchy of directories and files that appears to be one continuous file system,...
void remove_unused_textures(EggNode *node)
Removes any textures from the collection that aren't referenced by any primitives in the indicated eg...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
get_egg_filename
Returns the directory in which the egg file is considered to reside.
Definition: eggData.h:74
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_egg_filename
Sets the filename–especially the directory part–in which the egg file is considered to reside.
Definition: eggData.h:74
This is a collection of textures by TRef name.
int collapse_equivalent_materials(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate materials that are equivalent accord...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
set_egg_timestamp
Sets the timestamp of the egg file on disk, at the time it was opened for reading.
Definition: eggData.h:75
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
int extract_textures(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggTextures encountered in t...
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
EggGroupNode::iterator insert_materials(EggGroupNode *node)
Adds a series of EggMaterial nodes to the beginning of the indicated node to reflect each of the mate...
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
virtual void write(std::ostream &out, int indent_level) const
Writes the group and all of its children to the indicated output stream in Egg format.
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
This is a specialization of EggNameUniquifier to generate unique names for textures,...
EggGroupNode::iterator insert_textures(EggGroupNode *node)
Adds a series of EggTexture nodes to the beginning of the indicated node to reflect each of the textu...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light mutex.
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
void steal_children(EggGroupNode &other)
Moves all the children from the other node to this one.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
int extract_materials(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggMaterials encountered in ...
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
void uniquify(EggNode *node)
Begins the traversal from the indicated node.
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
void remove_unused_materials(EggNode *node)
Removes any materials from the collection that aren't referenced by any primitives in the indicated e...
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
std::ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written,...
set_coordinate_system
Changes the coordinate system of the EggData.
Definition: eggData.h:73
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_auto_resolve_externals
Indicates whether the EggData object will automatically resolve any external references when read() i...
Definition: eggData.h:72
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_coordinate_system
Returns the coordinate system in which the egg file is defined.
Definition: eggData.h:73
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool has_absolute_pathnames() const
Returns true if any nodes at this level and below include a reference to a file via an absolute pathn...
int find_used_materials(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for materials that are referenced by...
int collapse_equivalent_materials()
Removes duplicate references to the same material with the same properties.
Definition: eggData.cxx:201
void resolve_filenames(const DSearchPath &searchpath)
Walks the tree and attempts to resolve any filenames encountered.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void merge(EggData &other)
Appends the other egg structure to the end of this one.
Definition: eggData.cxx:139