Panda3D
eggQtess.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 eggQtess.cxx
10  * @author drose
11  * @date 2003-10-13
12  */
13 
14 #include "eggQtess.h"
15 #include "qtessGlobals.h"
16 #include "dcast.h"
17 
18 /**
19  *
20  */
21 EggQtess::
22 EggQtess() {
24 
25  set_program_brief("tesselate NURBS surfaces in .egg files");
26  set_program_description
27  ("egg-qtess reads an egg file, tessellates all of its NURBS surfaces "
28  "using a simple uniform tessellation, and outputs a polygonal "
29  "egg file.\n\n"
30 
31  "Characters are supported, soft-skinned and otherwise; joint "
32  "ownership is computed correctly for each new polygon vertex. "
33  "Primitives other than NURBS surfaces appearing in the egg file "
34  "are unaffected.");
35 
36  add_option
37  ("f", "filename", 0,
38  "Read the indicated parameter file. Type egg-qtess -H "
39  "to print a description of the parameter file format.",
40  &EggQtess::dispatch_filename, nullptr, &_qtess_filename);
41 
42  add_option
43  ("up", "subdiv", 0,
44  "Specify a uniform subdivision per patch (isoparam). Each NURBS "
45  "surface is made up of N x M patches, each of which is divided "
46  "into subdiv x subdiv quads. A fractional number is allowed.",
47  &EggQtess::dispatch_double, nullptr, &_uniform_per_isoparam);
48 
49  add_option
50  ("us", "subdiv", 0,
51  "Specify a uniform subdivision per surface. Each NURBS "
52  "surface is subdivided into subdiv x subdiv quads, regardless "
53  "of the number of isoparams it has. A fractional number is "
54  "meaningless.",
55  &EggQtess::dispatch_int, nullptr, &_uniform_per_surface);
56 
57  add_option
58  ("t", "tris", 0,
59  "Specify an approximate number of triangles to produce. This "
60  "is the total number of triangles for the entire egg file, "
61  "including those surfaces that have already been given an "
62  "explicit tessellation by a parameter file.",
63  &EggQtess::dispatch_int, nullptr, &_total_tris);
64 
65  add_option
66  ("ap", "", 0,
67  "Attempt to automatically place tessellation lines where they'll "
68  "do the most good on each surface (once the number of polygons "
69  "for the surface has already been determined).",
70  &EggQtess::dispatch_none, &QtessGlobals::_auto_place);
71 
72  add_option
73  ("ad", "", 0,
74  "Attempt to automatically distribute polygons among the surfaces "
75  "where they are most needed according to curvature and size, "
76  "instead of according to the number of isoparams. This only has "
77  "meaning when used in conjunction with -t.",
78  &EggQtess::dispatch_none, &QtessGlobals::_auto_distribute);
79 
80  add_option
81  ("ar", "ratio", 0,
82  "Specify the ratio of dominance of size to curvature for -ap and "
83  "-ad. A value of 0 forces placement by curvature only; a very "
84  "large value (like 1000) forces placement by size only. The "
85  "default is 5.0.",
86  &EggQtess::dispatch_double, nullptr, &QtessGlobals::_curvature_ratio);
87 
88  add_option
89  ("e", "", 0,
90  "Respect subdivision parameters given in the egg file. If this "
91  "is specified, the egg file may define the effective number of "
92  "patches of each NURBS entry. This can be used alone or in "
93  "conjunction with -u or -t to fine-tune the uniform tessellation "
94  "on a per-surface basis. (This is ignored if -ad is in effect.)",
95  &EggQtess::dispatch_none, &QtessGlobals::_respect_egg);
96 
97  add_option
98  ("q", "", 0,
99  "Instead of writing an egg file, generate a parameter file "
100  "for output.",
101  &EggQtess::dispatch_none, &_qtess_output);
102 
103  add_option
104  ("H", "", 0,
105  "Describe the format of the parameter file specified with -f.",
106  &EggQtess::dispatch_none, &_describe_qtess);
107 
108  _uniform_per_isoparam = 0.0;
109  _uniform_per_surface = 0;
110  _total_tris = 0;
111 }
112 
113 /**
114  * Does something with the additional arguments on the command line (after all
115  * the -options have been parsed). Returns true if the arguments are good,
116  * false otherwise.
117  */
118 bool EggQtess::
119 handle_args(ProgramBase::Args &args) {
120  if (_describe_qtess) {
121  describe_qtess_format();
122  exit(0);
123  }
124 
125  return EggFilter::handle_args(args);
126 }
127 
128 /**
129  *
130  */
131 void EggQtess::
132 run() {
133  bool read_qtess = false;
134  if (!_qtess_filename.empty()) {
135  if (!_qtess_file.read(_qtess_filename)) {
136  exit(1);
137  }
138  read_qtess = true;
139  }
140 
141  find_surfaces(_data);
142 
143  QtessInputEntry &default_entry = _qtess_file.get_default_entry();
144  if (!read_qtess || default_entry.get_num_surfaces() == 0) {
145  nout << _surfaces.size() << " NURBS surfaces found.\n";
146 
147  } else {
148  nout << _surfaces.size() << " NURBS surfaces found; "
149  << default_entry.get_num_surfaces()
150  << " unaccounted for by input file.\n";
151  }
152 
153  int num_tris = _qtess_file.count_tris();
154 
155  if (_total_tris != 0) {
156  // Whatever number of triangles we have unaccounted for, assign to the
157  // default bucket.
158  int extra_tris = std::max(0, _total_tris - num_tris);
159  if (read_qtess && default_entry.get_num_surfaces() != 0) {
160  std::cerr << extra_tris << " triangles unaccounted for.\n";
161  }
162 
163  default_entry.set_num_tris(extra_tris);
164 
165  } else if (_uniform_per_isoparam!=0.0) {
166  default_entry.set_per_isoparam(_uniform_per_isoparam);
167 
168  } else if (_uniform_per_surface!=0.0) {
169  default_entry.set_uv(_uniform_per_surface, _uniform_per_surface);
170 
171  } else {
172  default_entry.set_per_isoparam(1.0);
173  }
174 
175  default_entry.count_tris();
176 
177  if (_qtess_output) {
178  // Sort the names into alphabetical order for aesthetics.
179  // sort(_surfaces.begin(), _surfaces.end(), compare_surfaces());
180 
181  int tris = 0;
182 
183  std::ostream &out = get_output();
184  Surfaces::const_iterator si;
185  for (si = _surfaces.begin(); si != _surfaces.end(); ++si) {
186  tris += (*si)->write_qtess_parameter(out);
187  }
188 
189  std::cerr << tris << " tris generated.\n";
190 
191  } else {
192 
193  int tris = 0;
194 
195  Surfaces::const_iterator si;
196  for (si = _surfaces.begin(); si != _surfaces.end(); ++si) {
197  tris += (*si)->tesselate();
198  }
199 
200  std::cerr << tris << " tris generated.\n";
201 
202  // Clear out the surfaces list before removing the vertices, since each
203  // surface is holding reference counts to the previously-used vertices.
204  _surfaces.clear();
205 
206  _data->remove_unused_vertices(true);
207  write_egg_file();
208  }
209 }
210 
211 /**
212  *
213  */
214 void EggQtess::
215 describe_qtess_format() {
216  nout <<
217  "An egg-qtess parameter file consists of lines of the form:\n\n"
218 
219  "name [name...] : parameters\n\n"
220 
221  "Where name is a string (possibly including wildcard characters "
222  "such as * and ?) that matches one or more surface "
223  "names, and parameters is a tesselation specification, described below. "
224  "The colon must be followed by at least one space to differentiate it "
225  "from a colon character in the name(s). Multiple names "
226  "may be combined on one line.\n\n\n"
227 
228 
229  "The parameters may be any of the following. Lowercase letters are "
230  "literal. NUM is any number.\n\n";
231 
232  show_text(" omit", 10,
233  "Remove the surface from the output.\n\n");
234 
235  show_text(" NUM", 10,
236  "Try to achieve the indicated number of triangles over all the "
237  "surfaces matched by this line.\n\n");
238 
239  show_text(" NUM NUM [[!]u# [!]u# ...] [[!]v# [!]v# ...]", 10,
240  "Tesselate to NUM x NUM quads. If u# or v# appear, they indicate "
241  "additional isoparams to insert (or remove if preceded by an "
242  "exclamation point). The range is [0, 1].\n\n");
243 
244  show_text(" iNUM", 10,
245  "Subdivision amount per isoparam. Equivalent to the command-line "
246  "option -u NUM.\n\n");
247 
248  show_text(" NUM%", 10,
249  "This is a special parameter. This does not request any specific "
250  "tesselation for the named surfaces, but instead gives a relative "
251  "importance for them when they appear with other surfaces in a "
252  "later entry (or are tesselated via -t on the command line). In "
253  "general, a surface with a weight of 25% will be given a quarter "
254  "of the share of the polygons it otherwise would have received; "
255  "a weight of 150% will give the surface 50% more than its fair "
256  "share.\n\n");
257 
258  show_text(" matchvu", 10,
259  "This is a special parameter that indicates that two or more "
260  "surfaces share a common edge, and must be tesselated the "
261  "same way "
262  "along that edge. Specifically, matchvu means that the V "
263  "tesselation of the first named surface will be applied to the U "
264  "tesselation of the second (and later) named surface(s). Similar "
265  "definitions exist for matchuv, matchuu, and matchvv.\n\n");
266 
267  show_text(" minu NUM", 10,
268  "This is another special parameter that specifies a "
269  "minimum tesselation for all these surfaces in "
270  "the U direction. This is "
271  "the number of quads across the dimension the surface will be "
272  "broken into. The default is 1 for an open surface, and 3 for "
273  "a closed surface.\n\n");
274 
275  show_text(" minv NUM", 10,
276  "Similar to minv, in the V direction.\n\n");
277 
278  nout <<
279  "In addition, the following optional parameters may appear. If they appear, "
280  "they override similar parameters given on the command line; if they do not "
281  "appear, the defaults are taken from the command line:\n\n";
282 
283  show_text(" ap", 10,
284  "Automatically place tesselation lines on each surface where they "
285  "seem to be needed most.\n\n");
286 
287  show_text(" !ap", 10,
288  "Do not move lines automatically; use a strict uniform "
289  "tesselation.\n\n");
290 
291  show_text(" ad", 10,
292  "Automatically distribute polygons to the surfaces that seem to "
293  "need them the most.\n\n");
294 
295  show_text(" !ad", 10,
296  "Do not automatically distribute polygons; distribute "
297  "them according to the number of isoparams of each surface.\n\n");
298 
299  show_text(" arNUM", 10,
300  "Specify the ratio of dominance of size to curvature.\n\n");
301 
302  nout <<
303  "The hash symbol '#' begins a comment if it is preceded by whitespace or at the "
304  "beginning of a line. The backslash character at the end of a line can be used "
305  "to indicate a continuation.\n\n";
306 }
307 
308 /**
309  * Recursively walks the egg graph, collecting all the NURBS surfaces found.
310  */
311 void EggQtess::
312 find_surfaces(EggNode *egg_node) {
313  if (egg_node->is_of_type(EggNurbsSurface::get_class_type())) {
314  PT(QtessSurface) surface =
315  new QtessSurface(DCAST(EggNurbsSurface, egg_node));
316  if (surface->is_valid()) {
317  _surfaces.push_back(surface);
318  QtessInputEntry::Type match_type = _qtess_file.match(surface);
319  nassertv(match_type != QtessInputEntry::T_undefined);
320  }
321  }
322 
323  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
324  EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
325  EggGroupNode::const_iterator ci;
326  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
327  find_surfaces(*ci);
328  }
329  }
330 }
331 
332 int main(int argc, char *argv[]) {
333  EggQtess prog;
334  prog.parse_command_line(argc, argv);
335  prog.run();
336  return 0;
337 }
WithOutputFile::get_output
std::ostream & get_output()
Returns an output stream that corresponds to the user's intended egg file output–either stdout,...
Definition: withOutputFile.cxx:50
EggBase::add_normals_options
void add_normals_options()
Adds -no, -np, etc.
Definition: eggBase.cxx:59
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
QtessSurface
A reference to an EggNurbsSurface in the egg file, and its parameters as set by the user input file a...
Definition: qtessSurface.h:32
QtessInputFile::count_tris
int count_tris()
Determines the tesselation u,v amounts of each attached surface, and stores this information in the s...
Definition: qtessInputFile.cxx:297
QtessInputEntry::count_tris
int count_tris(double tri_factor=1.0, int attempts=0)
Determines the tesselation u,v amounts of each attached surface, and stores this information in the s...
Definition: qtessInputEntry.cxx:294
EggGroupNode
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggQtess
A program to tesselate NURBS surfaces appearing within an egg file into polygons, using variations on...
Definition: eggQtess.h:28
QtessInputFile::match
QtessInputEntry::Type match(QtessSurface *surface)
Attempts to find a match for the given surface in the user input entries.
Definition: qtessInputFile.cxx:273
ProgramBase::parse_command_line
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_...
Definition: programBase.cxx:274
pdeque< std::string >
QtessInputFile::get_default_entry
QtessInputEntry & get_default_entry()
Returns a reference to the last entry on the list, which is the "default" entry that will match any s...
Definition: qtessInputFile.cxx:254
eggQtess.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
QtessInputFile::read
bool read(const Filename &filename)
reads the input file.
Definition: qtessInputFile.cxx:31
QtessInputEntry
Stores one entry in the qtess input file.
Definition: qtessInputEntry.h:27
ProgramBase::show_text
void show_text(const std::string &text)
Formats the indicated text to stderr with the known _terminal_width.
Definition: programBase.I:18
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
EggWriter::write_egg_file
void write_egg_file()
Writes out the egg file as the normal result of the program.
Definition: eggWriter.cxx:177
qtessGlobals.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNurbsSurface
A parametric NURBS surface.
Definition: eggNurbsSurface.h:27
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28