Panda3D

qtessSurface.cxx

00001 // Filename: qtessSurface.cxx
00002 // Created by:  drose (13Oct03)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "qtessSurface.h"
00016 #include "qtessGlobals.h"
00017 #include "qtessInputEntry.h"
00018 #include "config_egg_qtess.h"
00019 #include "eggPolygon.h"
00020 #include "eggVertexPool.h"
00021 #include "eggVertex.h"
00022 #include "eggComment.h"
00023 #include "egg_parametrics.h"
00024 #include "pset.h"
00025 #include "pmap.h"
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: QtessSurface::Constructor
00029 //       Access: Public
00030 //  Description:
00031 ////////////////////////////////////////////////////////////////////
00032 QtessSurface::
00033 QtessSurface(EggNurbsSurface *egg_surface) :
00034   _egg_surface(egg_surface)
00035 {
00036   _nurbs = make_nurbs_surface(_egg_surface, LMatrix4d::ident_mat());
00037   _has_vertex_color = _egg_surface->has_vertex_color();
00038 
00039   // The first four slots are reserved for vertex color.
00040   _next_d = 4;
00041 
00042   _importance = 1.0;
00043   _importance2 = 1.0;
00044   _match_u = _match_v = NULL;
00045   _tess_u = _tess_v = 0;
00046   _got_scores = false;
00047 
00048   // If the surface is closed in either dimension, the mininum
00049   // tesselation in that dimension is by default 3, so we don't
00050   // ribbonize the surface.  Otherwise the minimum is 1.
00051   _min_u = _min_v = 1;
00052   if (egg_surface->is_closed_u()) {
00053     _min_u = 3;
00054   }
00055   if (egg_surface->is_closed_v()) {
00056     _min_v = 3;
00057   }
00058 
00059   if (_nurbs == (NurbsSurfaceEvaluator *)NULL) {
00060     _num_u = _num_v = 0;
00061 
00062   } else {
00063     record_vertex_extras();
00064 
00065     _nurbs->normalize_u_knots();
00066     _nurbs->normalize_v_knots();
00067     _nurbs_result = _nurbs->evaluate();
00068 
00069     _num_u = _nurbs->get_num_u_segments();
00070     _num_v = _nurbs->get_num_v_segments();
00071 
00072     if (QtessGlobals::_respect_egg) {
00073       if (egg_surface->get_u_subdiv() != 0) {
00074         _num_u = egg_surface->get_u_subdiv();
00075       }
00076       if (egg_surface->get_v_subdiv() != 0) {
00077         _num_v = egg_surface->get_v_subdiv();
00078       }
00079     }
00080   }
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: get_score
00085 //       Access: Public
00086 //  Description: Computes the curvature/stretch score for the surface,
00087 //               if it has not been already computed, and returns the
00088 //               net surface score.  This is used both for
00089 //               automatically distributing isoparams among the
00090 //               surfaces by curvature, as well as for automatically
00091 //               placing the isoparams within each surface by
00092 //               curvature.
00093 ////////////////////////////////////////////////////////////////////
00094 double QtessSurface::
00095 get_score(double ratio) {
00096   if (_nurbs == (NurbsSurfaceEvaluator *)NULL) {
00097     return 0.0;
00098   }
00099 
00100   if (!_got_scores) {
00101     _u_placer.get_scores(_nurbs->get_num_u_segments() * 100, 
00102                          _nurbs->get_num_v_segments() * 2,
00103                          ratio, _nurbs_result, true);
00104     _v_placer.get_scores(_nurbs->get_num_v_segments() * 100, 
00105                          _nurbs->get_num_u_segments() * 2,
00106                          ratio, _nurbs_result, false);
00107     _got_scores = true;
00108   }
00109 
00110   return _u_placer.get_total_score() * _v_placer.get_total_score() * _importance2;
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //     Function: QtessSurface::tesselate
00115 //       Access: Public
00116 //  Description: Applies the appropriate tesselation to the surface,
00117 //               and replaces its node in the tree with an EggGroup
00118 //               containing both the new vertex pool and all of the
00119 //               polygons.
00120 ////////////////////////////////////////////////////////////////////
00121 int QtessSurface::
00122 tesselate() {
00123   apply_match();
00124   int tris = 0;
00125 
00126   PT(EggGroup) group = do_uniform_tesselate(tris);
00127   PT(EggNode) new_node = group.p();
00128   if (new_node == (EggNode *)NULL) {
00129     new_node = new EggComment(_egg_surface->get_name(),
00130                               "Omitted NURBS surface.");
00131     tris = 0;
00132   }
00133   EggGroupNode *parent = _egg_surface->get_parent();
00134   nassertr(parent != (EggGroupNode *)NULL, 0);
00135   parent->remove_child(_egg_surface);
00136   parent->add_child(new_node);
00137 
00138   return tris;
00139 }
00140 
00141 ////////////////////////////////////////////////////////////////////
00142 //     Function: QtessSurface::write_qtess_parameter
00143 //       Access: Public
00144 //  Description: Writes a line to the given output file telling qtess
00145 //               how this surface should be tesselated uniformly.
00146 //               Returns the number of tris.
00147 ////////////////////////////////////////////////////////////////////
00148 int QtessSurface::
00149 write_qtess_parameter(ostream &out) {
00150   apply_match();
00151 
00152   if (_tess_u == 0 || _tess_v == 0) {
00153     out << get_name() << " : omit\n";
00154 
00155   } else if (_iso_u.empty() || _iso_v.empty()) {
00156     out << get_name() << " : " << _tess_u << " " << _tess_v << "\n";
00157 
00158   } else {
00159     out << get_name() << " : " << _iso_u.back() << " " << _iso_v.back();
00160     QtessInputEntry::output_extra(out, _iso_u, 'u');
00161     QtessInputEntry::output_extra(out, _iso_v, 'v');
00162     out << "\n";
00163   }
00164 
00165   return count_tris();
00166 }
00167 
00168 
00169 ////////////////////////////////////////////////////////////////////
00170 //     Function: QtessSurface::omit
00171 //       Access: Public
00172 //  Description: Sets up the surface to omit itself from the output.
00173 ////////////////////////////////////////////////////////////////////
00174 void QtessSurface::
00175 omit() {
00176   _tess_u = 0;
00177   _tess_v = 0;
00178 }
00179 
00180 ////////////////////////////////////////////////////////////////////
00181 //     Function: QtessSurface::tesselate_uv
00182 //       Access: Public
00183 //  Description: Sets the surface up to tesselate itself uniformly at
00184 //               u x v, or if autoplace is true, automatically with u
00185 //               x v quads.
00186 ////////////////////////////////////////////////////////////////////
00187 void QtessSurface::
00188 tesselate_uv(int u, int v, bool autoplace, double ratio) {
00189   _tess_u = u;
00190   _tess_v = v;
00191   _iso_u.clear();
00192   _iso_v.clear();
00193   if (autoplace) {
00194     tesselate_auto(_tess_u, _tess_v, ratio);
00195   }
00196 }
00197 
00198 ////////////////////////////////////////////////////////////////////
00199 //     Function: QtessSurface::tesselate_specific
00200 //       Access: Public
00201 //  Description: Sets the surface up to tesselate itself at specific
00202 //               isoparams only.
00203 ////////////////////////////////////////////////////////////////////
00204 void QtessSurface::
00205 tesselate_specific(const pvector<double> &u_list,
00206                    const pvector<double> &v_list) {
00207   _iso_u = u_list;
00208   _iso_v = v_list;
00209   _tess_u = (int)_iso_u.size() - 1;
00210   _tess_v = (int)_iso_v.size() - 1;
00211 }
00212 
00213 ////////////////////////////////////////////////////////////////////
00214 //     Function: QtessSurface::tesselate_per_isoparam
00215 //       Access: Public
00216 //  Description: Sets the surface up to tesselate itself to a uniform
00217 //               amount per isoparam.
00218 ////////////////////////////////////////////////////////////////////
00219 void QtessSurface::
00220 tesselate_per_isoparam(double pi, bool autoplace, double ratio) {
00221   if (_num_u == 0 || _num_v == 0) {
00222     omit();
00223 
00224   } else {
00225     _tess_u = max(_min_u, (int)floor(_num_u * _importance * pi + 0.5));
00226     _tess_v = max(_min_v, (int)floor(_num_v * _importance * pi + 0.5));
00227     _iso_u.clear();
00228     _iso_v.clear();
00229     if (autoplace) {
00230       tesselate_auto(_tess_u, _tess_v, ratio);
00231     }
00232   }
00233 }
00234 
00235 
00236 ////////////////////////////////////////////////////////////////////
00237 //     Function: QtessSurface::TesselatePerScore
00238 //       Access: Public
00239 //  Description: Sets the surface up to tesselate itself according to
00240 //               its computed curvature score in both dimensions.
00241 ////////////////////////////////////////////////////////////////////
00242 void QtessSurface::
00243 tesselate_per_score(double pi, bool autoplace, double ratio) {
00244   if (get_score(ratio) <= 0.0) {
00245     omit();
00246 
00247   } else {
00248     _tess_u = max(_min_u, (int)floor(_u_placer.get_total_score() * _importance * pi + 0.5));
00249     _tess_v = max(_min_v, (int)floor(_v_placer.get_total_score() * _importance * pi + 0.5));
00250     _iso_u.clear();
00251     _iso_v.clear();
00252     if (autoplace) {
00253       tesselate_auto(_tess_u, _tess_v, ratio);
00254     }
00255   }
00256 }
00257 
00258 ////////////////////////////////////////////////////////////////////
00259 //     Function: QtessSurface::tesselate_auto
00260 //       Access: Public
00261 //  Description: Sets the surface up to tesselate itself by
00262 //               automatically determining the best place to put the
00263 //               indicated u x v isoparams.
00264 ////////////////////////////////////////////////////////////////////
00265 void QtessSurface::
00266 tesselate_auto(int u, int v, double ratio) {
00267   if (get_score(ratio) <= 0.0) {
00268     omit();
00269 
00270   } else {
00271     _u_placer.place(u, _iso_u);
00272     _v_placer.place(v, _iso_v);
00273     _tess_u = (int)_iso_u.size() - 1;
00274     _tess_v = (int)_iso_v.size() - 1;
00275   }
00276 }
00277 
00278 ////////////////////////////////////////////////////////////////////
00279 //     Function: QtessSurface::record_vertex_extras
00280 //       Access: Private
00281 //  Description: Records the joint membership and morph offsets of
00282 //               each control vertex in the extra-dimensional space of
00283 //               the NURBS, so that we can extract this data out again
00284 //               later to apply to the polygon vertices.
00285 ////////////////////////////////////////////////////////////////////
00286 void QtessSurface::
00287 record_vertex_extras() {
00288   int num_u_vertices = _egg_surface->get_num_u_cvs();
00289   int num_v_vertices = _egg_surface->get_num_v_cvs();
00290 
00291   for (int ui = 0; ui < num_u_vertices; ui++) {
00292     for (int vi = 0; vi < num_v_vertices; vi++) {
00293       int i = _egg_surface->get_vertex_index(ui, vi);
00294       EggVertex *egg_vertex = _egg_surface->get_vertex(i);
00295 
00296       // The joint membership.
00297       EggVertex::GroupRef::const_iterator gi;
00298       for (gi = egg_vertex->gref_begin(); gi != egg_vertex->gref_end(); ++gi) {
00299         EggGroup *joint = (*gi);
00300         int d = get_joint_membership_index(joint);
00301         double membership = joint->get_vertex_membership(egg_vertex);
00302         _nurbs->set_extended_vertex(ui, vi, d, membership);
00303       }
00304 
00305       // The xyz morphs.
00306       EggMorphVertexList::const_iterator dxi;
00307       for (dxi = egg_vertex->_dxyzs.begin(); 
00308            dxi != egg_vertex->_dxyzs.end(); 
00309            ++dxi) {
00310         const string &morph_name = (*dxi).get_name();
00311         LVector3 delta = LCAST(PN_stdfloat, (*dxi).get_offset());
00312         int d = get_dxyz_index(morph_name);
00313         _nurbs->set_extended_vertices(ui, vi, d, delta.get_data(), 3);
00314       }
00315 
00316       // The rgba morphs.
00317       EggMorphColorList::const_iterator dri;
00318       for (dri = egg_vertex->_drgbas.begin(); 
00319            dri != egg_vertex->_drgbas.end(); 
00320            ++dri) {
00321         const string &morph_name = (*dri).get_name();
00322         const LVector4 &delta = (*dri).get_offset();
00323         int d = get_drgba_index(morph_name);
00324         _nurbs->set_extended_vertices(ui, vi, d, delta.get_data(), 4);
00325       }
00326     }
00327   }
00328 }
00329 
00330 ////////////////////////////////////////////////////////////////////
00331 //     Function: QtessSurface::apply_match
00332 //       Access: Private
00333 //  Description: If the surface was set up to copy its tesselation in
00334 //               either axis from another surface, makes this copy
00335 //               now.
00336 ////////////////////////////////////////////////////////////////////
00337 void QtessSurface::
00338 apply_match() {
00339   if (_match_u != NULL) {
00340     QtessSurface *m = *_match_u;
00341     if (m == NULL) {
00342       qtess_cat.warning()
00343         << "No surface to match " << get_name() << " to in U.\n";
00344     } else {
00345       if (qtess_cat.is_debug()) {
00346         qtess_cat.debug()
00347           << "Matching " << get_name() << " in U to " << m->get_name()
00348           << " in " << (_match_u_to_u?'U':'V') << ".\n";
00349       }
00350       if (_match_u_to_u) {
00351         _tess_u = m->_tess_u;
00352         _iso_u = m->_iso_u;
00353       } else {
00354         _tess_u = m->_tess_v;
00355         _iso_u = m->_iso_v;
00356       }
00357     }
00358   }
00359 
00360   if (_match_v != NULL) {
00361     QtessSurface *m = *_match_v;
00362     if (m == NULL) {
00363       qtess_cat.warning()
00364         << "No surface to match " << get_name() << " in V.\n";
00365     } else {
00366       if (qtess_cat.is_debug()) {
00367         qtess_cat.debug()
00368           << "Matching " << get_name() << " in V to " << m->get_name()
00369           << " in " << (_match_v_to_v?'V':'U') << ".\n";
00370       }
00371       if (_match_v_to_v) {
00372         _tess_v = m->_tess_v;
00373         _iso_v = m->_iso_v;
00374       } else {
00375         _tess_v = m->_tess_u;
00376         _iso_v = m->_iso_u;
00377       }
00378     }
00379   }
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 //     Function: do_uniform_tesselate
00384 //       Access: Private
00385 //  Description: Subdivide the surface uniformly according to the
00386 //               parameters specified by an earlier call to omit(),
00387 //               teseselate_uv(), or tesselate_per_isoparam().
00388 ////////////////////////////////////////////////////////////////////
00389 PT(EggGroup) QtessSurface::
00390 do_uniform_tesselate(int &tris) const {
00391   tris = 0;
00392 
00393   if (_tess_u == 0 || _tess_v == 0) {
00394     // No tesselation!
00395     if (qtess_cat.is_debug()) {
00396       qtess_cat.debug()
00397         << get_name() << " : omit\n";
00398     }
00399     return NULL;
00400   }
00401 
00402   PT(EggGroup) group = new EggGroup(_egg_surface->get_name());
00403 
00404   // _tess_u and _tess_v are the number of patches to create.  Convert
00405   // that to the number of vertices.
00406 
00407   int num_u = _tess_u + 1;
00408   int num_v = _tess_v + 1;
00409 
00410   if (qtess_cat.is_debug()) {
00411     qtess_cat.debug() << get_name() << " : " << tris << "\n";
00412   }
00413 
00414   assert(_iso_u.empty() || (int)_iso_u.size() == num_u);
00415   assert(_iso_v.empty() || (int)_iso_v.size() == num_v);
00416 
00417   // Now how many vertices is that total, and how many vertices per
00418   // strip?
00419   int num_verts = num_u * num_v;
00420 
00421   // Create a vertex pool.
00422   PT(EggVertexPool) vpool = new EggVertexPool(_egg_surface->get_name());
00423   group->add_child(vpool);
00424 
00425   // Create all the vertices.
00426   int ui, vi;
00427   double u, v;
00428 
00429   typedef pvector<EggVertex *> VertexList;
00430   VertexList new_verts;
00431   new_verts.reserve(num_verts);
00432 
00433   // Also collect the vertices into this set to group them by spatial
00434   // position only.  This is relevant for calculating normals.
00435   typedef pset<EggVertex *> NVertexGroup;
00436   typedef pmap<LVertexd, NVertexGroup> NVertexCollection;
00437   NVertexCollection n_collection;
00438   
00439   for (vi = 0; vi < num_v; vi++) {
00440     if (_iso_v.empty()) {
00441       v = (double)vi / (double)(num_v-1);
00442     } else {
00443       v = _iso_v[vi] / _iso_v.back();
00444     }
00445     for (ui = 0; ui < num_u; ui++) {
00446       if (_iso_u.empty()) {
00447         u = (double)ui / (double)(num_u-1);
00448       } else {
00449         u = _iso_u[ui] / _iso_u.back();
00450       }
00451 
00452       PT(EggVertex) egg_vertex = evaluate_vertex(u, v);
00453       vpool->add_vertex(egg_vertex);
00454       new_verts.push_back(egg_vertex);
00455       n_collection[egg_vertex->get_pos3()].insert(egg_vertex);
00456     }
00457   }
00458   nassertr((int)new_verts.size() == num_verts, NULL);
00459 
00460   // Now create a bunch of quads.
00461   for (vi = 1; vi < num_v; vi++) {
00462     for (ui = 1; ui < num_u; ui++) {
00463       PT(EggPolygon) poly = new EggPolygon;
00464       poly->add_vertex(new_verts[vi*num_u + (ui-1)]);
00465       poly->add_vertex(new_verts[(vi-1)*num_u + (ui-1)]);
00466       poly->add_vertex(new_verts[(vi-1)*num_u + ui]); 
00467       poly->add_vertex(new_verts[vi*num_u + ui]);
00468 
00469       poly->copy_attributes(*_egg_surface);
00470 
00471       // We compute a polygon normal just so we can verify the
00472       // calculated vertex normals.  It's also helpful for identifying
00473       // degenerate polygons.
00474       if (poly->recompute_polygon_normal()) {
00475         tris += 2;
00476         group->add_child(poly);
00477       }
00478     }
00479   }
00480 
00481   // Now check all the vertex normals by comparing them to the polygon
00482   // normals.  Some might have not been computed at all; others might
00483   // be facing in the wrong direction.
00484 
00485   // Now go back through and normalize the computed normals.
00486   NVertexCollection::const_iterator nci;
00487   for (nci = n_collection.begin(); nci != n_collection.end(); ++nci) {
00488     const NVertexGroup &group = (*nci).second;
00489 
00490     // Calculate the normal these vertices should have based on the
00491     // polygons that share it.
00492     LNormald normal = LNormald::zero();
00493     int num_polys = 0;
00494     NVertexGroup::const_iterator ngi;
00495     for (ngi = group.begin(); ngi != group.end(); ++ngi) {
00496       EggVertex *egg_vertex = (*ngi);
00497       EggVertex::PrimitiveRef::const_iterator pri;
00498       for (pri = egg_vertex->pref_begin(); 
00499            pri != egg_vertex->pref_end(); 
00500            ++pri) {
00501         EggPrimitive *egg_primitive = (*pri);
00502         nassertr(egg_primitive->has_normal(), NULL);
00503         normal += egg_primitive->get_normal();
00504         num_polys++;
00505       }
00506     }
00507 
00508     if (num_polys > 0) {
00509       normal /= (double)num_polys;
00510 
00511       // Now compare this normal with what the NURBS representation
00512       // calculated.  It should be facing in at least vaguely the same
00513       // direction.
00514       for (ngi = group.begin(); ngi != group.end(); ++ngi) {
00515         EggVertex *egg_vertex = (*ngi);
00516         if (egg_vertex->has_normal()) {
00517           if (normal.dot(egg_vertex->get_normal()) < 0.0) {
00518             // This one is backwards.
00519             egg_vertex->set_normal(-egg_vertex->get_normal());
00520           }
00521         } else {
00522           // This vertex doesn't have a normal; it gets the computed
00523           // normal.
00524           egg_vertex->set_normal(normal);
00525         }
00526       }
00527     }
00528   }
00529 
00530   return group;
00531 }
00532 
00533 ////////////////////////////////////////////////////////////////////
00534 //     Function: evaluate_vertex
00535 //       Access: Private
00536 //  Description: Evaluates the surface at the given u, v position and
00537 //               sets the vertex to the appropriate values.  Also sets
00538 //               the joint membership of the vertex.
00539 ////////////////////////////////////////////////////////////////////
00540 PT(EggVertex) QtessSurface::
00541 evaluate_vertex(double u, double v) const {
00542   PT(EggVertex) egg_vertex = new EggVertex;
00543 
00544   LVertex point;
00545   LNormal normal;
00546   _nurbs_result->eval_point(u, v, point);
00547   _nurbs_result->eval_normal(u, v, normal);
00548 
00549   // If the normal is too short, don't consider it--it's probably
00550   // inaccurate due to numerical limitations.  We'll recompute it
00551   // later based on the polygon normals.
00552   PN_stdfloat length = normal.length();
00553   if (length > 0.0001f) {
00554     normal /= length;
00555     egg_vertex->set_normal(LCAST(double, normal));
00556   }
00557 
00558   egg_vertex->set_pos(LCAST(double, point));
00559   egg_vertex->set_uv(LVecBase2d(u, v));
00560 
00561   // The color is stored, by convention, in slots 0-4 of the surface.
00562   if (_has_vertex_color) {
00563     LColor rgba;
00564     _nurbs_result->eval_extended_points(u, v, 0, &rgba[0], 4);
00565     egg_vertex->set_color(rgba);
00566   }
00567 
00568   // Also fill in the joint membership.
00569   JointTable::const_iterator jti;
00570   for (jti = _joint_table.begin(); jti != _joint_table.end(); ++jti) {
00571     EggGroup *joint = (*jti).first;
00572     int d = (*jti).second;
00573 
00574     double membership = _nurbs_result->eval_extended_point(u, v, d);
00575     if (membership > 0.0) {
00576       joint->ref_vertex(egg_vertex, membership);
00577     }
00578   }
00579 
00580   // And the morphs.
00581   MorphTable::const_iterator mti;
00582   for (mti = _dxyz_table.begin(); mti != _dxyz_table.end(); ++mti) {
00583     const string &morph_name = (*mti).first;
00584     int d = (*mti).second;
00585 
00586     LVector3 delta;
00587     _nurbs_result->eval_extended_points(u, v, d, &delta[0], 3);
00588     if (!delta.almost_equal(LVector3::zero())) {
00589       egg_vertex->_dxyzs.insert(EggMorphVertex(morph_name, LCAST(double, delta)));
00590     }
00591   }
00592 
00593   for (mti = _drgba_table.begin(); mti != _drgba_table.end(); ++mti) {
00594     const string &morph_name = (*mti).first;
00595     int d = (*mti).second;
00596 
00597     LVector4 delta;
00598     _nurbs_result->eval_extended_points(u, v, d, &delta[0], 4);
00599     if (!delta.almost_equal(LVector4::zero())) {
00600       egg_vertex->_drgbas.insert(EggMorphColor(morph_name, delta));
00601     }
00602   }
00603   
00604   return egg_vertex;
00605 }
 All Classes Functions Variables Enumerations