From 634a498588891da4640bb86a878b65612018ea2c Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 23 Mar 2005 15:32:35 +0000 Subject: [PATCH] egg triagle fans --- panda/src/egg/Sources.pp | 5 + panda/src/egg/config_egg.cxx | 76 ++++- panda/src/egg/eggCompositePrimitive.cxx | 74 +++-- panda/src/egg/eggMesher.cxx | 28 +- panda/src/egg/eggMesher.h | 3 - panda/src/egg/eggMesherFanMaker.I | 93 ++++++ panda/src/egg/eggMesherFanMaker.cxx | 371 ++++++++++++++++++++++++ panda/src/egg/eggMesherFanMaker.h | 80 +++++ panda/src/egg/eggMesherStrip.cxx | 7 +- panda/src/egg/eggMesherStrip.h | 2 +- panda/src/egg/eggTriangleFan.I | 47 +++ panda/src/egg/eggTriangleFan.cxx | 133 +++++++++ panda/src/egg/eggTriangleFan.h | 66 +++++ panda/src/egg/eggTriangleStrip.cxx | 4 - panda/src/egg/egg_composite1.cxx | 1 + panda/src/egg/egg_composite2.cxx | 1 + panda/src/egg/parser.yxx | 22 ++ panda/src/egg2pg/eggLoader.cxx | 5 + panda/src/gobj/qpgeomTrifans.cxx | 2 + 19 files changed, 961 insertions(+), 59 deletions(-) create mode 100644 panda/src/egg/eggMesherFanMaker.I create mode 100644 panda/src/egg/eggMesherFanMaker.cxx create mode 100644 panda/src/egg/eggMesherFanMaker.h create mode 100644 panda/src/egg/eggTriangleFan.I create mode 100644 panda/src/egg/eggTriangleFan.cxx create mode 100644 panda/src/egg/eggTriangleFan.h diff --git a/panda/src/egg/Sources.pp b/panda/src/egg/Sources.pp index 3a76fdf329..c7839b2334 100644 --- a/panda/src/egg/Sources.pp +++ b/panda/src/egg/Sources.pp @@ -25,6 +25,7 @@ eggMaterialCollection.h \ eggMesher.h eggMesher.I \ eggMesherEdge.h eggMesherEdge.I \ + eggMesherFanMaker.h eggMesherFanMaker.I \ eggMesherStrip.h eggMesherStrip.I \ eggMiscFuncs.I eggMiscFuncs.h \ eggMorph.I eggMorph.h eggMorphList.I eggMorphList.h \ @@ -38,6 +39,7 @@ eggSAnimData.I eggSAnimData.h eggSurface.I eggSurface.h \ eggSwitchCondition.h eggTable.I eggTable.h eggTexture.I \ eggTexture.h eggTextureCollection.I eggTextureCollection.h \ + eggTriangleFan.I eggTriangleFan.h \ eggTriangleStrip.I eggTriangleStrip.h \ eggTransform3d.I eggTransform3d.h \ eggUserData.I eggUserData.h \ @@ -61,6 +63,7 @@ eggMaterialCollection.cxx \ eggMesher.cxx \ eggMesherEdge.cxx \ + eggMesherFanMaker.cxx \ eggMesherStrip.cxx \ eggMiscFuncs.cxx eggMorphList.cxx \ eggNamedObject.cxx eggNameUniquifier.cxx eggNode.cxx \ @@ -70,6 +73,7 @@ eggSAnimData.cxx eggSurface.cxx eggSwitchCondition.cxx \ eggTable.cxx eggTexture.cxx eggTextureCollection.cxx \ eggTransform3d.cxx \ + eggTriangleFan.cxx \ eggTriangleStrip.cxx \ eggUserData.cxx \ eggUtilities.cxx eggVertex.cxx eggVertexPool.cxx eggVertexUV.cxx \ @@ -101,6 +105,7 @@ eggSwitchCondition.h eggTable.I eggTable.h eggTexture.I \ eggTexture.h eggTextureCollection.I eggTextureCollection.h \ eggTransform3d.I eggTransform3d.h \ + eggTriangleFan.I eggTriangleFan.h \ eggTriangleStrip.I eggTriangleStrip.h \ eggUserData.I eggUserData.h \ eggUtilities.I eggUtilities.h eggVertex.I eggVertex.h \ diff --git a/panda/src/egg/config_egg.cxx b/panda/src/egg/config_egg.cxx index 4062560adc..b27c4d38b7 100644 --- a/panda/src/egg/config_egg.cxx +++ b/panda/src/egg/config_egg.cxx @@ -47,6 +47,7 @@ #include "eggSwitchCondition.h" #include "eggTable.h" #include "eggTexture.h" +#include "eggTriangleFan.h" #include "eggTriangleStrip.h" #include "eggUserData.h" #include "eggVertex.h" @@ -75,27 +76,79 @@ ConfigVariableBool egg_support_old_anims "had the convention that the order \"phr\" implied a reversed roll.")); ConfigVariableBool egg_mesh -("egg-mesh", true); +("egg-mesh", true, + PRC_DESC("Set this true to convert triangles and higher-order polygons " + "into triangle strips and triangle fans when an egg file is " + "loaded or converted to bam. Set this false just to triangulate " + "everything into independent triangles.")); + ConfigVariableBool egg_retesselate_coplanar -("egg-retesselate-coplanar", true); +("egg-retesselate-coplanar", true, + PRC_DESC("If this is true, the egg loader may reverse the " + "tesselation direction of a single pair of planar triangles that " + "share the same properties, if that will help get a better " + "triangle strip. In some rare cases, doing so can distort the " + "UV's on a face; turning this off should eliminate that artifact " + "(at the cost of less-effective triangle stripping).")); + ConfigVariableBool egg_unroll_fans -("egg-unroll-fans", true); +("egg-unroll-fans", true, + PRC_DESC("Set this true to allow the egg loader to convert weak triangle " + "fans--triangles that share the same vertex but aren't " + "connected enough to justify making a triangle fan primitive " + "from them--into a series of zig-zag triangles that can make " + "a triangle strip that might connect better with its neighbors.")); + ConfigVariableBool egg_show_tstrips -("egg-show-tstrips", false); +("egg-show-tstrips", false, + PRC_DESC("Set this true to color each triangle strip a random color, with " + "the leading triangle a little bit darker, so you can visually " + "observe the quality of the triangle stripping algorithm.")); + ConfigVariableBool egg_show_qsheets -("egg-show-qsheets", false); +("egg-show-qsheets", false, + PRC_DESC("Set this true to color each quadsheet a random color, so you " + "can visually observe the quadsheet algorithm.")); + ConfigVariableBool egg_show_quads -("egg-show-quads", false); +("egg-show-quads", false, + PRC_DESC("Set this true to color each detected quad a random color, so " + "you can visually observe the algorithm that unifies pairs of " + "triangles into quads (prior to generating triangle strips).")); + ConfigVariableBool egg_subdivide_polys -("egg-subdivide-polys", true); +("egg-subdivide-polys", true, + PRC_DESC("This is obsolete. In the old Geom implementation, it used to " + "be true to force higher-order polygons that were not otherwise " + "meshed to be subdivided into triangles. In the new experimental " + "Geom implementation, this happens anyway.")); + ConfigVariableBool egg_consider_fans -("egg-consider-fans", true); +("egg-consider-fans", true, + PRC_DESC("Set this true to enable the egg mesher to consider making " + "triangle fans out of triangles that are connected at a common " + "vertex. This may help if your scene involves lots of such " + "connected triangles, but it can also make the overall stripping " + "less effective (by interfering with triangle strips).")); + ConfigVariableDouble egg_max_tfan_angle -("egg-max-tfan-angle", 40.0); +("egg-max-tfan-angle", 40.0, + PRC_DESC("The maximum average angle per triangle to allow in a triangle " + "fan. If triangles are larger than this--that is, more loosely " + "packed--then we figure a triangle strip is likely to do a " + "more effective job than a triangle fan, and the fan maker leaves " + "it alone.")); + ConfigVariableInt egg_min_tfan_tris -("egg-min-tfan-tris", 4); +("egg-min-tfan-tris", 4, + PRC_DESC("The minimum number of triangles that must be involved in order " + "to generate a triangle fan. Fewer than this is just interrupting " + "a triangle strip.")); + ConfigVariableDouble egg_coplanar_threshold -("egg-coplanar-threshold", 0.01); +("egg-coplanar-threshold", 0.01, + PRC_DESC("The numerical threshold below which polygons are considered " + "to be coplanar. Determined empirically.")); //////////////////////////////////////////////////////////////////// // Function: init_libegg @@ -145,6 +198,7 @@ init_libegg() { EggSwitchConditionDistance::init_type(); EggTable::init_type(); EggTexture::init_type(); + EggTriangleFan::init_type(); EggTriangleStrip::init_type(); EggUserData::init_type(); EggVertex::init_type(); diff --git a/panda/src/egg/eggCompositePrimitive.cxx b/panda/src/egg/eggCompositePrimitive.cxx index 57c2f9f1b3..7a94f19c76 100644 --- a/panda/src/egg/eggCompositePrimitive.cxx +++ b/panda/src/egg/eggCompositePrimitive.cxx @@ -157,15 +157,32 @@ unify_attributes(EggPrimitive::Shading shading) { shading = get_shading(); } - // Not having a color is implicitly white. - if (!has_color() && shading != S_overall) { - set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); - } - switch (shading) { case S_per_vertex: // Propagate everything to the vertices. { + Components::iterator ci; + for (ci = _components.begin(); ci != _components.end(); ++ci) { + EggAttributes *component = (*ci); + if (component->has_normal()) { + if (!has_normal()) { + copy_normal(*component); + } + component->clear_normal(); + } + if (component->has_color()) { + if (!has_color()) { + copy_color(*component); + } + component->clear_color(); + } + } + + // Not having a color is implicitly white. + if (!has_color()) { + set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); + } + iterator pi; for (pi = begin(); pi != end(); ++pi) { EggVertex *orig_vertex = (*pi); @@ -182,11 +199,6 @@ unify_attributes(EggPrimitive::Shading shading) { vertex = vertex_pool->create_unique_vertex(*vertex); replace(pi, vertex); } - Components::iterator ci; - for (ci = _components.begin(); ci != _components.end(); ++ci) { - (*ci)->clear_normal(); - (*ci)->clear_color(); - } clear_normal(); clear_color(); } @@ -195,20 +207,17 @@ unify_attributes(EggPrimitive::Shading shading) { case S_per_face: // Propagate everything to the components. { - Components::iterator ci; - for (ci = _components.begin(); ci != _components.end(); ++ci) { - EggAttributes *component = (*ci); - if (!component->has_normal() && has_normal()) { - component->copy_normal(*this); - } - if (!component->has_color() && has_color()) { - component->copy_color(*this); - } - } iterator pi; for (pi = begin(); pi != end(); ++pi) { EggVertex *orig_vertex = (*pi); if (orig_vertex->has_normal() || orig_vertex->has_color()) { + if (orig_vertex->has_normal() && !has_normal()) { + copy_normal(*orig_vertex); + } + if (orig_vertex->has_color() && !has_color()) { + copy_color(*orig_vertex); + } + PT(EggVertex) vertex = new EggVertex(*orig_vertex); vertex->clear_normal(); vertex->clear_color(); @@ -219,6 +228,22 @@ unify_attributes(EggPrimitive::Shading shading) { replace(pi, vertex); } } + + // Not having a color is implicitly white. + if (!has_color()) { + set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); + } + + Components::iterator ci; + for (ci = _components.begin(); ci != _components.end(); ++ci) { + EggAttributes *component = (*ci); + if (!component->has_normal() && has_normal()) { + component->copy_normal(*this); + } + if (!component->has_color() && has_color()) { + component->copy_color(*this); + } + } clear_normal(); clear_color(); } @@ -259,16 +284,17 @@ unify_attributes(EggPrimitive::Shading shading) { component->clear_color(); } } + + // Not having a color is implicitly white. + if (!has_color()) { + set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); + } } break; case S_unknown: break; } - - if (!has_color() && shading == S_overall) { - set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); - } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/egg/eggMesher.cxx b/panda/src/egg/eggMesher.cxx index d5be310b35..05d1f9fbe7 100644 --- a/panda/src/egg/eggMesher.cxx +++ b/panda/src/egg/eggMesher.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "eggMesher.h" +#include "eggMesherFanMaker.h" #include "eggPolygon.h" #include "eggCompositePrimitive.h" #include "eggTriangleStrip.h" @@ -80,7 +81,7 @@ mesh(EggGroupNode *group) { add_polygon(poly, EggMesherStrip::MO_user); } else { - // A different vertex pool; save this one for next pass. + // A different vertex pool; save this one for the next pass. next_children->add_child(poly); } @@ -320,8 +321,8 @@ get_prim(EggMesherStrip &strip) { Colorf color1, color2; - if (egg_prim->is_of_type(EggTriangleStrip::get_class_type()) /*|| - egg_prim->is_of_type(EggTriangleFan::get_class_type())*/) { + if (egg_prim->is_of_type(EggTriangleStrip::get_class_type()) || + egg_prim->is_of_type(EggTriangleFan::get_class_type())) { make_random_color(color2); color1 = (color2 * 0.8); // somewhat darker. @@ -621,8 +622,7 @@ build_sheets() { //////////////////////////////////////////////////////////////////// void EggMesher:: find_fans() { -#if 0 - pvector unrolled_tris; + PT(EggGroupNode) unrolled_tris = new EggGroup; // Consider all vertices. Any vertex with over a certain number of // edges connected to it is eligible to become a fan. @@ -639,22 +639,21 @@ find_fans() { // the quadsheets anyway. We bump this up to 14 because some // quadsheets are defined with triangles flipped here and there. if (edges.size() > 6) { - const Vertex &v = (*vi).first; + int v = (*vi).first; // Build up a list of far fan edges. - typedef pvector FanMakers; - + typedef pvector FanMakers; FanMakers fans; EdgePtrs::iterator ei; - Edge::Strips::iterator si; + EggMesherEdge::Strips::iterator si; for (ei = edges.begin(); ei != edges.end(); ++ei) { for (si = (*ei)->_strips.begin(); si != (*ei)->_strips.end(); ++si) { EggMesherStrip *strip = *si; if (strip->_type == EggMesherStrip::PT_tri) { - FanMaker fan(&v, strip, this); + EggMesherFanMaker fan(v, strip, this); if (!fan._edges.empty()) { fans.push_back(fan); } @@ -670,7 +669,7 @@ find_fans() { FanMakers::iterator fi, fi2; // Now pull out connected edges. - int joined_any; + bool joined_any; do { joined_any = false; for (fi = fans.begin(); fi != fans.end(); ++fi) { @@ -698,11 +697,10 @@ find_fans() { // until we're done traversing all the vertices and primitives we // had in the first place (since adding them will affect the edge // lists). - pvector::iterator ti; - for (ti = unrolled_tris.begin(); ti != unrolled_tris.end(); ++ti) { - add_prim(*ti); + EggGroupNode::iterator ti; + for (ti = unrolled_tris->begin(); ti != unrolled_tris->end(); ++ti) { + add_polygon(DCAST(EggPolygon, (*ti)), EggMesherStrip::MO_fanpoly); } -#endif } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/egg/eggMesher.h b/panda/src/egg/eggMesher.h index 92e5c189f7..92db50bf42 100644 --- a/panda/src/egg/eggMesher.h +++ b/panda/src/egg/eggMesher.h @@ -22,7 +22,6 @@ #include "pandabase.h" #include "eggMesherEdge.h" #include "eggMesherStrip.h" -//#include "eggMesherFanMaker.h" #include "eggPolygon.h" #include "pvector.h" #include "plist.h" @@ -31,8 +30,6 @@ #include -class MesherFanMaker; - /////////////////////////////////////////////////////////////////// // Class : EggMesher // Description : Collects together unrelated EggPrimitives, determines diff --git a/panda/src/egg/eggMesherFanMaker.I b/panda/src/egg/eggMesherFanMaker.I new file mode 100644 index 0000000000..5efe5e96cb --- /dev/null +++ b/panda/src/egg/eggMesherFanMaker.I @@ -0,0 +1,93 @@ +// Filename: eggMesherFanMaker.I +// Created by: drose (22Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::operator < +// Access: Public +// Description: Provides a unique ordering between different fan +// makers based on the leading edge. +//////////////////////////////////////////////////////////////////// +INLINE bool EggMesherFanMaker:: +operator < (const EggMesherFanMaker &other) const { + nassertr(!_edges.empty() && !other._edges.empty(), false); + return _edges.front() < other._edges.front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::operator != +// Access: Public +// Description: Provides a unique ordering between different fan +// makers based on the leading edge. +//////////////////////////////////////////////////////////////////// +INLINE bool EggMesherFanMaker:: +operator != (const EggMesherFanMaker &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::operator == +// Access: Public +// Description: Provides a unique ordering between different fan +// makers based on the leading edge. +//////////////////////////////////////////////////////////////////// +INLINE bool EggMesherFanMaker:: +operator == (const EggMesherFanMaker &other) const { + return _edges.front() == other._edges.front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::is_empty +// Access: Public +// Description: Returns true if the fan maker has no edges, false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EggMesherFanMaker:: +is_empty() const { + return (_edges.empty()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::is_valid +// Access: Public +// Description: Returns true if the fan maker has enough edges to +// define at least one fan, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EggMesherFanMaker:: +is_valid() const { + return (_edges.size() > 2); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::is_coplanar_with +// Access: Public +// Description: Returns true if the strip and the other strip are +// coplanar. +//////////////////////////////////////////////////////////////////// +INLINE bool EggMesherFanMaker:: +is_coplanar_with(const EggMesherFanMaker &other) const { + return _planar && other._planar && + _strips.front()->is_coplanar_with(*other._strips.front(), + egg_coplanar_threshold); +} + +INLINE ostream & +operator << (ostream &out, const EggMesherFanMaker &fm) { + fm.output(out); + return out; +} diff --git a/panda/src/egg/eggMesherFanMaker.cxx b/panda/src/egg/eggMesherFanMaker.cxx new file mode 100644 index 0000000000..175a004fa5 --- /dev/null +++ b/panda/src/egg/eggMesherFanMaker.cxx @@ -0,0 +1,371 @@ +// Filename: eggMesherFanMaker.cxx +// Created by: drose (22Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "eggMesherFanMaker.h" + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggMesherFanMaker:: +EggMesherFanMaker(int vertex, EggMesherStrip *tri, + EggMesher *mesher) { + _vertex = vertex; + const EggMesherEdge *edge = tri->find_opposite_edge(vertex); + if (edge != (const EggMesherEdge *)NULL) { + _edges.push_back(edge); + } + _strips.push_back(tri); + _planar = tri->_planar; + _mesher = mesher; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggMesherFanMaker:: +EggMesherFanMaker(const EggMesherFanMaker ©) : + _vertex(copy._vertex), + _edges(copy._edges), + _strips(copy._strips), + _planar(copy._planar), + _mesher(copy._mesher) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggMesherFanMaker:: +operator = (const EggMesherFanMaker ©) { + _vertex = copy._vertex; + _edges = copy._edges; + _strips = copy._strips; + _planar = copy._planar; + _mesher = copy._mesher; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::join +// Access: Public +// Description: Attempts to connect two fans end-to-end. They must +// both share the same common vertex and a common edge. +// +// The return value is true if the fans were +// successfully joined, or false if they could not be. +//////////////////////////////////////////////////////////////////// +bool EggMesherFanMaker:: +join(EggMesherFanMaker &other) { + nassertr(_vertex == other._vertex, false); + nassertr(_mesher == other._mesher, false); + + nassertr(!_edges.empty() && !other._edges.empty(), false); + + const EggMesherEdge *my_back = _edges.back(); + const EggMesherEdge *other_front = other._edges.front(); + nassertr(my_back != (EggMesherEdge *)NULL && + other_front != (EggMesherEdge *)NULL, false); + + int my_back_b = my_back->_vi_b; + int other_front_a = other_front->_vi_a; + + if (my_back_b == other_front_a) { + _planar = is_coplanar_with(other); + _edges.splice(_edges.end(), other._edges); + _strips.splice(_strips.end(), other._strips); + return true; + } + + const EggMesherEdge *my_front = _edges.front(); + const EggMesherEdge *other_back = other._edges.back(); + nassertr(my_front != (EggMesherEdge *)NULL && + other_back != (EggMesherEdge *)NULL, false); + + int my_front_a = my_front->_vi_a; + int other_back_b = other_back->_vi_b; + + if (my_front_a == other_back_b) { + _planar = is_coplanar_with(other); + _edges.splice(_edges.begin(), other._edges); + _strips.splice(_strips.begin(), other._strips); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::compute_angle +// Access: Public +// Description: Returns the overall angle subtended by the fan, from +// the leading edge to the trailing edge, in degrees. +//////////////////////////////////////////////////////////////////// +double EggMesherFanMaker:: +compute_angle() const { + // We sum up the angles of each triangle. This is more correct than + // taking the net angle from the first edge to the last (since we + // may not be in a plane). + nassertr(is_valid(), 0.0); + + EggVertexPool *vertex_pool = _mesher->_vertex_pool; + + double angle = 0.0; + LPoint3d v0 = vertex_pool->get_vertex(_vertex)->get_pos3(); + + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + LVector3d v1 = vertex_pool->get_vertex((*ei)->_vi_a)->get_pos3() - v0; + LVector3d v2 = vertex_pool->get_vertex((*ei)->_vi_b)->get_pos3() - v0; + + v1 = normalize(v1); + v2 = normalize(v2); + angle += acos(dot(v1, v2)); + } + + return rad_2_deg(angle); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::build +// Access: Public +// Description: Begins the fanning process. Searches for triangles +// and connects them into a fan. +// +// In certain cases, if egg_unroll_fans is enabled, the +// resulting fan may be retesselated into a series of +// zig-zag triangles, which are stored in unrolled_tris. +// Otherwise, an EggMesherStrip (representing the fan) +// is created and added to the mesher. +// +// The return value is (loosely) the number of +// primitives created. +//////////////////////////////////////////////////////////////////// +int EggMesherFanMaker:: +build(EggGroupNode *unrolled_tris) { + nassertr(_edges.size() == _strips.size(), 0); + + int num_tris = _edges.size(); + double net_angle = compute_angle(); + double avg_angle = net_angle / (double)num_tris; + + if (avg_angle > egg_max_tfan_angle) { + // The triangles are too loose to justify making a fan; it'll + // probably make a better quadsheet. + return 0; + } + + if (egg_min_tfan_tris == 0 || num_tris < egg_min_tfan_tris) { + // Oops, not enough triangles to justify a fan. + if (!egg_unroll_fans) { + return 0; + } + + // However, we could (maybe) make it a few tristrips! + + // Each section of the fan which is made up of coplanar tris, with + // identical properties, may be retesselated into a tristrip. + // What a sneaky trick! To do this, we must first identify each + // such qualifying section. + + // We define a seam as the edge between any two tris which are + // noncoplanar or have different properties. Then we can send + // each piece between the seams to unroll(). + + Strips::iterator si, last_si; + Edges::iterator ei, last_ei; + + // First, rotate the fan so it begins at a seam. We do this so we + // won't be left out with part of one piece at the beginning and + // also at the end. + si = _strips.begin(); + last_si = si; + ei = _edges.begin(); + last_ei = ei; + int found_seam = false; + + for (++si, ++ei; si != _strips.end() && !found_seam; ++si, ++ei) { + nassertr(ei != _edges.end(), 0); + if ( ((*si)->_prims.front()->compare_to(*(*last_si)->_prims.front()) != 0) || + !(*si)->is_coplanar_with(*(*last_si), egg_coplanar_threshold)) { + // Here's a seam. Break the fan here. + found_seam = true; + _edges.splice(_edges.begin(), _edges, ei, _edges.end()); + _strips.splice(_strips.begin(), _strips, si, _strips.end()); + } + } + + // Now break the fan up along its seams and unroll each piece + // separately. + si = _strips.begin(); + last_si = si; + ei = _edges.begin(); + last_ei = ei; + + int count = 0; + for (++si, ++ei; si != _strips.end(); ++si, ++ei) { + nassertr(ei != _edges.end(), 0); + if ( ((*si)->_prims.front()->compare_to(*(*last_si)->_prims.front()) != 0) || + !(*si)->is_coplanar_with(*(*last_si), egg_coplanar_threshold)) { + // Here's the end of a run of matching pieces. + count += unroll(last_si, si, last_ei, ei, unrolled_tris); + last_si = si; + last_ei = ei; + } + } + count += unroll(last_si, si, last_ei, ei, unrolled_tris); + + return count; + + } else { + EggMesherStrip new_fan; + new_fan._type = EggMesherStrip::PT_trifan; + new_fan._origin = EggMesherStrip::MO_fanpoly; + new_fan._verts.push_back(_vertex); + + new_fan._verts.push_back(_edges.front()->_vi_a); + Edges::iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + new_fan._verts.push_back((*ei)->_vi_b); + } + + Strips::iterator si; + for (si = _strips.begin(); si != _strips.end(); ++si) { + new_fan._prims.splice(new_fan._prims.end(), (*si)->_prims); + (*si)->remove_all_edges(); + (*si)->_verts.clear(); + (*si)->_status = EggMesherStrip::MS_dead; + } + + // If we'd built our list of edges and strips right, this sum should + // come out so that there are two more vertices than triangles in + // the new fan. + nassertr(new_fan._verts.size() == new_fan._prims.size() + 2, 0); + + // Now we've built a fan, and it won't be able to mate with + // anything else, so add it to the done list. + _mesher->_done.push_back(new_fan); + } + + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::unroll +// Access: Public +// Description: Unrolls a planar subset of the current working fan, +// defined by the given iterators, into a series of +// triangles that zig-zag back and forth for better +// tristripping properties. The new triangles are added +// to unrolled_tris; the return value is 1 if +// successful, or 0 otherwise. +//////////////////////////////////////////////////////////////////// +int EggMesherFanMaker:: +unroll(Strips::iterator strip_begin, Strips::iterator strip_end, + Edges::iterator edge_begin, Edges::iterator edge_end, + EggGroupNode *unrolled_tris) { + Edges::iterator ei; + Strips::iterator si; + + int num_tris = 0; + for (ei = edge_begin; ei != edge_end; ++ei) { + num_tris++; + } + + if (num_tris < 3) { + // Don't even bother. + return 0; + } + + PT(EggPolygon) poly = new EggPolygon; + + // Now we build an n-sided polygon the same shape as the fan. We'll + // decompose it into tris in a second. + poly->copy_attributes(*(*strip_begin)->_prims.front()); + EggVertexPool *vertex_pool = _mesher->_vertex_pool; + + ei = edge_end; + --ei; + if ((*ei)->_vi_b != (*edge_begin)->_vi_a) { + // If the fan is less than a full circle, we need to keep the + // hub vertex and initial vertex in the poly. Otherwise, we'll + // discard them. + poly->add_vertex(vertex_pool->get_vertex(_vertex)); + poly->add_vertex(vertex_pool->get_vertex((*edge_begin)->_vi_a)); + } + + for (ei = edge_begin; ei != edge_end; ++ei) { + poly->add_vertex(vertex_pool->get_vertex((*ei)->_vi_b)); + } + + bool result = true; + + if (egg_show_quads) { + // If we're showing quads, also show retesselated triangles. + + // We can't add it directly to the mesher, that's unsafe; instead, + // we'll just add it to the end of the unrolled_tris list. This + // does mean we won't be able to color it a fancy color, but too + // bad. + //_mesher->add_polygon(poly, EggMesherStrip::MO_fanpoly); + unrolled_tris->add_child(poly); + + } else { + // Now decompose the new polygon into triangles. + result = poly->triangulate_into(unrolled_tris, true); + } + + if (result) { + // Now that we've created a new poly, kill off all the old ones. + for (si = strip_begin; si != strip_end; ++si) { + (*si)->remove_all_edges(); + (*si)->_verts.clear(); + (*si)->_prims.clear(); + (*si)->_status = EggMesherStrip::MS_dead; + } + return 1; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggMesherFanMaker::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggMesherFanMaker:: +output(ostream &out) const { + out << _vertex << ":["; + if (!_edges.empty()) { + Edges::const_iterator ei; + for (ei = _edges.begin(); ei != _edges.end(); ++ei) { + out << " " << (*ei)->_vi_a; + } + out << " " << _edges.back()->_vi_b; + } + out << " ]"; + + if (_planar) { + out << " (planar)"; + } +} diff --git a/panda/src/egg/eggMesherFanMaker.h b/panda/src/egg/eggMesherFanMaker.h new file mode 100644 index 0000000000..1286f3658e --- /dev/null +++ b/panda/src/egg/eggMesherFanMaker.h @@ -0,0 +1,80 @@ +// Filename: eggMesherFanMaker.h +// Created by: drose (22Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGMESHERFANMAKER_H +#define EGGMESHERFANMAKER_H + +#include "pandabase.h" + +#include "eggMesherEdge.h" +#include "eggMesherStrip.h" + +#include "plist.h" +#include "pvector.h" + +#include "notify.h" +#include "mathNumbers.h" + +class EggMesher; + +/////////////////////////////////////////////////////////////////// +// Class : EggMesherFanMaker +// Description : This class is used by EggMesher::find_fans() to +// attempt to make an EggTriangleFan out of the polygons +// connected to the indicated vertex. +//////////////////////////////////////////////////////////////////// +class EggMesherFanMaker { +public: + typedef plist Edges; + typedef plist Strips; + + EggMesherFanMaker(int vertex, EggMesherStrip *tri, + EggMesher *mesher); + EggMesherFanMaker(const EggMesherFanMaker ©); + void operator = (const EggMesherFanMaker ©); + + INLINE bool operator < (const EggMesherFanMaker &other) const; + INLINE bool operator != (const EggMesherFanMaker &other) const; + INLINE bool operator == (const EggMesherFanMaker &other) const; + + INLINE bool is_empty() const; + INLINE bool is_valid() const; + INLINE bool is_coplanar_with(const EggMesherFanMaker &other) const; + + bool join(EggMesherFanMaker &other); + double compute_angle() const; + + int build(EggGroupNode *unrolled_tris); + int unroll(Strips::iterator strip_begin, Strips::iterator strip_end, + Edges::iterator edge_begin, Edges::iterator edge_end, + EggGroupNode *unrolled_tris); + + void output(ostream &out) const; + + int _vertex; + Edges _edges; + Strips _strips; + bool _planar; + EggMesher *_mesher; +}; + +INLINE ostream &operator << (ostream &out, const EggMesherFanMaker &fm); + +#include "eggMesherFanMaker.I" + +#endif diff --git a/panda/src/egg/eggMesherStrip.cxx b/panda/src/egg/eggMesherStrip.cxx index 8e1588dd3f..1c87ec1a2c 100644 --- a/panda/src/egg/eggMesherStrip.cxx +++ b/panda/src/egg/eggMesherStrip.cxx @@ -19,6 +19,7 @@ #include "eggMesherStrip.h" #include "eggMesherEdge.h" #include "eggPrimitive.h" +#include "eggTriangleFan.h" #include "eggTriangleStrip.h" #include "eggPolygon.h" #include "dcast.h" @@ -115,7 +116,11 @@ make_prim(const EggVertexPool *vertex_pool) { } else { // The harder case: a tristrip of some kind. convert_to_type(dest_type); - prim = new EggTriangleStrip; + if (dest_type == PT_trifan) { + prim = new EggTriangleFan; + } else { + prim = new EggTriangleStrip; + } prim->copy_attributes(*_prims.front()); // Now store all the vertices. Each individual triangle's diff --git a/panda/src/egg/eggMesherStrip.h b/panda/src/egg/eggMesherStrip.h index 131223000c..9707b774b4 100644 --- a/panda/src/egg/eggMesherStrip.h +++ b/panda/src/egg/eggMesherStrip.h @@ -142,7 +142,7 @@ public: int _index; MesherStatus _status; - int _planar; + bool _planar; Normald _plane_normal; float _plane_offset; int _row_id, _row_distance; diff --git a/panda/src/egg/eggTriangleFan.I b/panda/src/egg/eggTriangleFan.I new file mode 100644 index 0000000000..68037e570f --- /dev/null +++ b/panda/src/egg/eggTriangleFan.I @@ -0,0 +1,47 @@ +// Filename: eggTriangleFan.I +// Created by: drose (23Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTriangleFan:: +EggTriangleFan(const string &name) : EggCompositePrimitive(name) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::Copy constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTriangleFan:: +EggTriangleFan(const EggTriangleFan ©) : EggCompositePrimitive(copy) { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::Copy assignment operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTriangleFan &EggTriangleFan:: +operator = (const EggTriangleFan ©) { + EggCompositePrimitive::operator = (copy); + return *this; +} diff --git a/panda/src/egg/eggTriangleFan.cxx b/panda/src/egg/eggTriangleFan.cxx new file mode 100644 index 0000000000..673e82246d --- /dev/null +++ b/panda/src/egg/eggTriangleFan.cxx @@ -0,0 +1,133 @@ +// Filename: eggTriangleFan.cxx +// Created by: drose (23Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "eggTriangleFan.h" +#include "eggGroupNode.h" +#include "eggPolygon.h" +#include "indent.h" + +TypeHandle EggTriangleFan::_type_handle; + + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::Destructor +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +EggTriangleFan:: +~EggTriangleFan() { + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::write +// Access: Published, Virtual +// Description: Writes the triangle fan to the indicated output +// stream in Egg format. +//////////////////////////////////////////////////////////////////// +void EggTriangleFan:: +write(ostream &out, int indent_level) const { + write_header(out, indent_level, ""); + write_body(out, indent_level+2); + indent(out, indent_level) << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::apply_first_attribute +// Access: Published, Virtual +// Description: Sets the first vertex of the triangle (or each +// component) to the primitive normal and/or color, if +// the primitive is flat-shaded. This reflects the +// DirectX convention of storing flat-shaded properties +// on the first vertex, although it is not usually a +// convention in Egg. +// +// This may introduce redundant vertices to the vertex +// pool. +//////////////////////////////////////////////////////////////////// +void EggTriangleFan:: +apply_first_attribute() { + // In the case of a triangle fan, the first vertex of the fan is the + // common vertex, so we consider the second vertex to be the key + // vertex of the first triangle, and move from there. + for (int i = 0; i < get_num_components(); i++) { + EggAttributes *component = get_component(i); + do_apply_flat_attribute(i + 1, component); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::get_num_lead_vertices +// Access: Protected, Virtual +// Description: Returns the number of initial vertices that are not +// used in defining any component; the first component +// is defined by the (n + 1)th vertex, and then a new +// component at each vertex thereafter. +//////////////////////////////////////////////////////////////////// +int EggTriangleFan:: +get_num_lead_vertices() const { + return 2; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleFan::triangulate_poly +// Access: Protected, Virtual +// Description: Fills the container up with EggPolygons that +// represent the component triangles of this triangle +// fan. +// +// It is assumed that the EggTriangleFan is not +// already a child of any other group when this function +// is called. +// +// Returns true if the triangulation is successful, or +// false if there was some error (in which case the +// container may contain some partial triangulation). +//////////////////////////////////////////////////////////////////// +bool EggTriangleFan:: +do_triangulate(EggGroupNode *container) const { + if (size() < 3) { + return false; + } + const_iterator vi = begin(); + EggVertex *v0 = (*vi); + ++vi; + EggVertex *v1 = (*vi); + ++vi; + + for (int i = 0; i < (int)size() - 2; i++) { + PT(EggPolygon) poly = new EggPolygon; + poly->copy_attributes(*this); + const EggAttributes *attrib = get_component(i); + if (attrib->has_color()) { + poly->set_color(attrib->get_color()); + } + if (attrib->has_normal()) { + poly->set_normal(attrib->get_normal()); + } + + poly->add_vertex(v0); + poly->add_vertex(v1); + poly->add_vertex(*vi); + v1 = *vi; + container->add_child(poly); + ++vi; + } + + return true; +} diff --git a/panda/src/egg/eggTriangleFan.h b/panda/src/egg/eggTriangleFan.h new file mode 100644 index 0000000000..b0ef408b1e --- /dev/null +++ b/panda/src/egg/eggTriangleFan.h @@ -0,0 +1,66 @@ +// Filename: eggTriangleFan.h +// Created by: drose (23Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGTRIANGLEFAN_H +#define EGGTRIANGLEFAN_H + +#include "pandabase.h" + +#include "eggCompositePrimitive.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggTriangleFan +// Description : A connected fan of triangles. This does not +// normally appear in an egg file; it is typically +// generated as a result of meshing. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEGG EggTriangleFan : public EggCompositePrimitive { +PUBLISHED: + INLINE EggTriangleFan(const string &name = ""); + INLINE EggTriangleFan(const EggTriangleFan ©); + INLINE EggTriangleFan &operator = (const EggTriangleFan ©); + virtual ~EggTriangleFan(); + + virtual void write(ostream &out, int indent_level) const; + virtual void apply_first_attribute(); + +protected: + virtual int get_num_lead_vertices() const; + virtual bool do_triangulate(EggGroupNode *container) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EggCompositePrimitive::init_type(); + register_type(_type_handle, "EggTriangleFan", + EggCompositePrimitive::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "eggTriangleFan.I" + +#endif diff --git a/panda/src/egg/eggTriangleStrip.cxx b/panda/src/egg/eggTriangleStrip.cxx index 7fa1a4a800..cd9520b661 100644 --- a/panda/src/egg/eggTriangleStrip.cxx +++ b/panda/src/egg/eggTriangleStrip.cxx @@ -19,12 +19,8 @@ #include "eggTriangleStrip.h" #include "eggGroupNode.h" #include "eggPolygon.h" -#include "plane.h" - #include "indent.h" -#include - TypeHandle EggTriangleStrip::_type_handle; diff --git a/panda/src/egg/egg_composite1.cxx b/panda/src/egg/egg_composite1.cxx index 803c6eac66..84415611b5 100644 --- a/panda/src/egg/egg_composite1.cxx +++ b/panda/src/egg/egg_composite1.cxx @@ -18,6 +18,7 @@ #include "eggMaterialCollection.cxx" #include "eggMesher.cxx" #include "eggMesherEdge.cxx" +#include "eggMesherFanMaker.cxx" #include "eggMesherStrip.cxx" #include "eggMiscFuncs.cxx" #include "eggMorphList.cxx" diff --git a/panda/src/egg/egg_composite2.cxx b/panda/src/egg/egg_composite2.cxx index 02dd103040..9fd5537bca 100644 --- a/panda/src/egg/egg_composite2.cxx +++ b/panda/src/egg/egg_composite2.cxx @@ -13,6 +13,7 @@ #include "eggTexture.cxx" #include "eggTextureCollection.cxx" #include "eggTransform3d.cxx" +#include "eggTriangleFan.cxx" #include "eggTriangleStrip.cxx" #include "eggUserData.cxx" #include "eggUtilities.cxx" diff --git a/panda/src/egg/parser.yxx b/panda/src/egg/parser.yxx index 42bd766b92..ac4bbafede 100644 --- a/panda/src/egg/parser.yxx +++ b/panda/src/egg/parser.yxx @@ -16,6 +16,7 @@ #include "eggVertexUV.h" #include "eggPolygon.h" #include "eggCompositePrimitive.h" +#include "eggTriangleFan.h" #include "eggTriangleStrip.h" #include "eggPoint.h" #include "eggLine.h" @@ -182,6 +183,7 @@ egg_cleanup_parser() { %type <_egg> joint %type <_egg> line %type <_egg> polygon +%type <_egg> trianglefan %type <_egg> trianglestrip %type <_egg> point_light %type <_egg> nurbs_surface @@ -252,6 +254,7 @@ node: | joint | instance | polygon + | trianglefan | trianglestrip | point_light | line @@ -1596,6 +1599,25 @@ polygon: } ; +/* + * trianglefan + * + * enter: + * exit: returns a new EggTriangleFan. + * + */ +trianglefan: + TRIANGLEFAN optional_name +{ + egg_stack.push_back(new EggTriangleFan($2)); +} + '{' primitive_body '}' +{ + $$ = egg_stack.back(); + egg_stack.pop_back(); +} + ; + /* * trianglestrip * diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 853639a5dc..eebdf2ce43 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -40,6 +40,7 @@ #include "qpgeom.h" #include "qpgeomTriangles.h" #include "qpgeomTristrips.h" +#include "qpgeomTrifans.h" #include "qpgeomLines.h" #include "qpgeomLinestrips.h" #include "qpgeomPoints.h" @@ -61,6 +62,7 @@ #include "eggGroup.h" #include "eggPolygon.h" #include "eggTriangleStrip.h" +#include "eggTriangleFan.h" #include "eggBin.h" #include "eggTable.h" #include "eggBinner.h" @@ -2000,6 +2002,9 @@ make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, } else if (egg_prim->is_of_type(EggTriangleStrip::get_class_type())) { primitive = new qpGeomTristrips(qpGeomUsageHint::UH_static); + } else if (egg_prim->is_of_type(EggTriangleFan::get_class_type())) { + primitive = new qpGeomTrifans(qpGeomUsageHint::UH_static); + } else if (egg_prim->is_of_type(EggLine::get_class_type())) { if (egg_prim->size() == 2) { primitive = new qpGeomLines(qpGeomUsageHint::UH_static); diff --git a/panda/src/gobj/qpgeomTrifans.cxx b/panda/src/gobj/qpgeomTrifans.cxx index 4e70ac8fa2..9433b0abc4 100644 --- a/panda/src/gobj/qpgeomTrifans.cxx +++ b/panda/src/gobj/qpgeomTrifans.cxx @@ -125,6 +125,8 @@ decompose_impl() const { triangles->add_vertex(v0); triangles->add_vertex(v1); triangles->add_vertex(vertices[vi]); + nassertr(vi < (int)vertices.size(), this); + v1 = vertices[vi]; triangles->close_primitive(); ++vi; }