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 }
A reference to an EggNurbsSurface in the egg file, and its parameters as set by the user input file a...
Definition: qtessSurface.h:32
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_...
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
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...
void add_normals_options()
Adds -no, -np, etc.
Definition: eggBase.cxx:59
QtessInputEntry::Type match(QtessSurface *surface)
Attempts to find a match for the given surface in the user input entries.
int count_tris()
Determines the tesselation u,v amounts of each attached surface, and stores this information in the s...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
QtessInputEntry & get_default_entry()
Returns a reference to the last entry on the list, which is the "default" entry that will match any s...
Stores one entry in the qtess input file.
void write_egg_file()
Writes out the egg file as the normal result of the program.
Definition: eggWriter.cxx:177
bool read(const Filename &filename)
reads the input file.
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
A parametric NURBS surface.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
A program to tesselate NURBS surfaces appearing within an egg file into polygons, using variations on...
Definition: eggQtess.h:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & get_output()
Returns an output stream that corresponds to the user's intended egg file output–either stdout,...
void show_text(const std::string &text)
Formats the indicated text to stderr with the known _terminal_width.
Definition: programBase.I:18