Преглед на файлове

more geometric font support

David Rose преди 19 години
родител
ревизия
91f22c514a
променени са 3 файла, в които са добавени 279 реда и са изтрити 63 реда
  1. 44 0
      panda/src/text/dynamicTextFont.I
  2. 220 61
      panda/src/text/dynamicTextFont.cxx
  3. 15 2
      panda/src/text/dynamicTextFont.h

+ 44 - 0
panda/src/text/dynamicTextFont.I

@@ -409,6 +409,50 @@ get_winding_order() const {
   return _winding_order;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::ContourPoint::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DynamicTextFont::ContourPoint::
+ContourPoint(const LPoint2f &p, const LVector2f &in, const LVector2f &out) :
+  _p(p), _in(in), _out(out)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::ContourPoint::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DynamicTextFont::ContourPoint::
+ContourPoint(float px, float py, float tx, float ty) :
+  _p(px, py), _in(tx, ty), _out(tx, ty)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::connect_to
+//       Access: Public
+//  Description: Connects the indicated point to the next point, whose
+//               tangent is given.  The given tangent becomes the out
+//               tangent at this point.  If the in tangent and out
+//               tangent are sufficiently close, they will be smoothed
+//               together.
+////////////////////////////////////////////////////////////////////
+INLINE void DynamicTextFont::ContourPoint::
+connect_to(const LVector2f &out) {
+  _out = out;
+  if (_in.dot(_out) > 0.7071) {
+    // Less than 45 degrees of difference: smooth them.
+    LVector2f av = (_in + _out) * 0.5f;
+    av.normalize();
+    _in = av;
+    _out = av;
+  }
+}
+
+
 INLINE ostream &
 operator << (ostream &out, const DynamicTextFont &dtf) {
   return out << dtf.get_name();

+ 220 - 61
panda/src/text/dynamicTextFont.cxx

@@ -47,6 +47,7 @@
 #include "nurbsCurveEvaluator.h"
 #include "nurbsCurveResult.h"
 //#include "renderModeAttrib.h"
+//#include "antialiasAttrib.h"
 
 TypeHandle DynamicTextFont::_type_handle;
 
@@ -430,7 +431,15 @@ make_glyph(int character, int glyph_index) {
       return glyph;
 
     case RM_polygon:
-      render_polygon_contours(glyph);
+      render_polygon_contours(glyph, true, false);
+      return glyph;
+
+    case RM_extruded:
+      render_polygon_contours(glyph, false, true);
+      return glyph;
+
+    case RM_solid:
+      render_polygon_contours(glyph, true, true);
       return glyph;
 
     default:
@@ -656,7 +665,7 @@ render_wireframe_contours(DynamicTextGlyph *glyph) {
     Points::const_iterator pi;
 
     for (pi = contour._points.begin(); pi != contour._points.end(); ++pi) {
-      const LPoint2f &p = (*pi);
+      const LPoint2f &p = (*pi)._p;
       vertex.add_data3f(p[0], 0.0f, p[1]);
     }
 
@@ -675,68 +684,167 @@ render_wireframe_contours(DynamicTextGlyph *glyph) {
 //               geometry, as a polygon render.
 ////////////////////////////////////////////////////////////////////
 void DynamicTextFont::
-render_polygon_contours(DynamicTextGlyph *glyph) {
+render_polygon_contours(DynamicTextGlyph *glyph, bool face, bool extrude) {
   PT(GeomVertexData) vdata = new GeomVertexData
-    (string(), GeomVertexFormat::get_v3(),
+    (string(), GeomVertexFormat::get_v3n3(),
      Geom::UH_static);
   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+  GeomVertexWriter normal(vdata, InternalName::get_normal());
 
   PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
   Triangulator t;
 
-  // First, build up the list of vertices, and determine which
-  // contours are solid and which are holes.
   Contours::iterator ci;
-  for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
-    Contour &contour = (*ci);
 
-    t.clear_polygon();
-    contour._start_vertex = t.get_num_vertices();
-    for (size_t i = 0; i < contour._points.size() - 1; ++i) {
-      const LPoint2f &p = contour._points[i];
-      vertex.add_data3f(p[0], 0.0f, p[1]);
-      int vi = t.add_vertex(p[0], p[1]);
-      t.add_polygon_vertex(vi);
+  if (face) {
+    // First, build up the list of vertices for the face, and
+    // determine which contours are solid and which are holes.
+    for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
+      Contour &contour = (*ci);
+      
+      t.clear_polygon();
+      contour._start_vertex = t.get_num_vertices();
+      for (size_t i = 0; i < contour._points.size() - 1; ++i) {
+        const LPoint2f &p = contour._points[i]._p;
+        vertex.add_data3f(p[0], 0.0f, p[1]);
+        normal.add_data3f(0.0f, -1.0f, 0.0f);
+        int vi = t.add_vertex(p[0], p[1]);
+        t.add_polygon_vertex(vi);
+      }
+      
+      contour._is_solid = t.is_left_winding();
     }
 
-    contour._is_solid = t.is_left_winding();
+    // Now go back and generate the actual triangles for the face.
+    for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
+      const Contour &contour = (*ci);
+      
+      if (contour._is_solid && !contour._points.empty()) {
+        t.clear_polygon();
+        for (size_t i = 0; i < contour._points.size() - 1; ++i) {
+          t.add_polygon_vertex(contour._start_vertex + i);
+        }
+        
+        // Also add all the holes to each polygon.
+        Contours::iterator cj;
+        for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
+          Contour &hole = (*cj);
+          if (!hole._is_solid && !hole._points.empty()) {
+            t.begin_hole();
+            for (size_t j = 0; j < hole._points.size() - 1; ++j) {
+              t.add_hole_vertex(hole._start_vertex + j);
+            }
+          }
+        }
+        
+        t.triangulate();
+        int num_triangles = t.get_num_triangles();
+        for (int ti = 0; ti < num_triangles; ++ti) {
+          tris->add_vertex(t.get_triangle_v0(ti));
+          tris->add_vertex(t.get_triangle_v1(ti));
+          tris->add_vertex(t.get_triangle_v2(ti));
+          tris->close_primitive();
+        }
+      }
+    }
   }
 
-  // Now go back and generate the actual triangles.
-  for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
-    const Contour &contour = (*ci);
-    
-    if (contour._is_solid && !contour._points.empty()) {
-      t.clear_polygon();
-      for (size_t i = 0; i < contour._points.size() - 1; ++i) {
-        t.add_polygon_vertex(contour._start_vertex + i);
+  if (extrude) {
+    // If we're generating extruded geometry (polygons along the
+    // edges, down the y axis), generate them now.  These are pretty
+    // easy, but we need to create more vertices--they don't share the
+    // same normals.
+    for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
+      const Contour &contour = (*ci);
+      Points::const_iterator pi;
+
+      for (size_t i = 0; i < contour._points.size(); ++i) {
+        const ContourPoint &cp = contour._points[i];
+        const LPoint2f &p = cp._p;
+        const LVector2f &t_in = cp._in;
+        const LVector2f &t_out = cp._out;
+
+        LVector3f n_in(t_in[1], 0.0f, -t_in[0]);
+        vertex.add_data3f(p[0], 1.0f, p[1]);
+        vertex.add_data3f(p[0], 0.0f, p[1]);
+        normal.add_data3f(n_in);
+        normal.add_data3f(n_in);
+
+        if (i != 0) {
+          int vi = vertex.get_write_row();
+          tris->add_vertex(vi - 4);
+          tris->add_vertex(vi - 2);
+          tris->add_vertex(vi - 1);
+          tris->close_primitive();
+          tris->add_vertex(vi - 1);
+          tris->add_vertex(vi - 3);
+          tris->add_vertex(vi - 4);
+          tris->close_primitive();
+        }
+
+        if (i != contour._points.size() - 1 && !t_in.almost_equal(t_out)) {
+          // If the out tangent is different from the in tangent, we
+          // need to store new vertices for the next quad.
+          LVector3f n_out(t_out[1], 0.0f, -t_out[0]);
+          vertex.add_data3f(p[0], 1.0f, p[1]);
+          vertex.add_data3f(p[0], 0.0f, p[1]);
+          normal.add_data3f(n_out);
+          normal.add_data3f(n_out);
+        }
       }
+    }
 
-      // Also add all the holes to each polygon.
-      Contours::iterator cj;
-      for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
-        Contour &hole = (*cj);
-        if (!hole._is_solid && !hole._points.empty()) {
-          t.begin_hole();
-          for (size_t j = 0; j < hole._points.size() - 1; ++j) {
-            t.add_hole_vertex(hole._start_vertex + j);
-          }
+    if (face) {
+      // Render the back side of the face too.
+      int back_start = vertex.get_write_row();
+
+      for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
+        Contour &contour = (*ci);
+        for (size_t i = 0; i < contour._points.size() - 1; ++i) {
+          const LPoint2f &p = contour._points[i]._p;
+          vertex.add_data3f(p[0], 1.0f, p[1]);
+          normal.add_data3f(0.0f, 1.0f, 0.0f);
         }
       }
 
-      t.triangulate();
-      int num_triangles = t.get_num_triangles();
-      for (int ti = 0; ti < num_triangles; ++ti) {
-        tris->add_vertex(t.get_triangle_v0(ti));
-        tris->add_vertex(t.get_triangle_v1(ti));
-        tris->add_vertex(t.get_triangle_v2(ti));
-        tris->close_primitive();
+      // Now go back and generate the actual triangles for the face.
+      for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
+        const Contour &contour = (*ci);
+      
+        if (contour._is_solid && !contour._points.empty()) {
+          t.clear_polygon();
+          for (size_t i = 0; i < contour._points.size() - 1; ++i) {
+            t.add_polygon_vertex(contour._start_vertex + i);
+          }
+        
+          // Also add all the holes to each polygon.
+          Contours::iterator cj;
+          for (cj = _contours.begin(); cj != _contours.end(); ++cj) {
+            Contour &hole = (*cj);
+            if (!hole._is_solid && !hole._points.empty()) {
+              t.begin_hole();
+              for (size_t j = 0; j < hole._points.size() - 1; ++j) {
+                t.add_hole_vertex(hole._start_vertex + j);
+              }
+            }
+          }
+        
+          t.triangulate();
+          int num_triangles = t.get_num_triangles();
+          for (int ti = 0; ti < num_triangles; ++ti) {
+            tris->add_vertex(t.get_triangle_v2(ti) + back_start);
+            tris->add_vertex(t.get_triangle_v1(ti) + back_start);
+            tris->add_vertex(t.get_triangle_v0(ti) + back_start);
+            tris->close_primitive();
+          }
+        }
       }
     }
   }
 
   glyph->set_geom(vdata, tris, RenderState::make_empty());
   //  glyph->set_geom(vdata, tris, RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_wireframe)));
+  //  glyph->set_geom(vdata, tris, RenderState::make(AntialiasAttrib::make(AntialiasAttrib::M_auto)));
   _contours.clear();
 }
 
@@ -754,8 +862,11 @@ outline_move_to(const FT_Vector *to, void *user) {
   float scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
   LPoint2f p = LPoint2f(to->x, to->y) * scale;
 
-  self->_contours.push_back(Contour());
-  self->_contours.back()._points.push_back(p);
+  if (self->_contours.empty() ||
+      !self->_contours.back()._points.empty()) {
+    self->_contours.push_back(Contour());
+  }
+  self->_q = p;
   return 0;
 }
 
@@ -774,7 +885,18 @@ outline_line_to(const FT_Vector *to, void *user) {
   float scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
   LPoint2f p = LPoint2f(to->x, to->y) * scale;
 
-  self->_contours.back()._points.push_back(p);
+  // Compute the tangent: this is just the vector from the last point.
+  LVector2f t = (p - self->_q);
+  t.normalize();
+
+  if (self->_contours.back()._points.empty()) {
+    self->_contours.back()._points.push_back(ContourPoint(self->_q, LVector2f::zero(), t));
+  } else {
+    self->_contours.back()._points.back().connect_to(t);
+  }
+  
+  self->_contours.back()._points.push_back(ContourPoint(p, t, LVector2f::zero()));
+  self->_q = p;
   return 0;
 }
 
@@ -789,7 +911,6 @@ outline_conic_to(const FT_Vector *control,
                  const FT_Vector *to, void *user) {
   DynamicTextFont *self = (DynamicTextFont *)user;
   nassertr(!self->_contours.empty(), 1);
-  const LPoint2f &q = self->_contours.back()._points.back();
 
   // Convert from 26.6 pixel units to Panda units.
   float scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
@@ -802,22 +923,14 @@ outline_conic_to(const FT_Vector *control,
   nce.local_object();
   nce.set_order(3);
   nce.reset(3);
-  nce.set_vertex(0, LVecBase3f(q[0], q[1], 0.0f));
+  nce.set_vertex(0, LVecBase3f(self->_q[0], self->_q[1], 0.0f));
   nce.set_vertex(1, LVecBase3f(c[0], c[1], 0.0f));
   nce.set_vertex(2, LVecBase3f(p[0], p[1], 0.0f));
 
-  PT(NurbsCurveResult) ncr = nce.evaluate();
-
-  // Sample it down so that the lines approximate the curve to within
-  // a "pixel."
-  ncr->adaptive_sample(1.0f / self->_font_pixels_per_unit);
+  self->_q = p;
 
-  int num_samples = ncr->get_num_samples();
-  for (int i = 1; i < num_samples; ++i) {
-    const LPoint3f &p = ncr->get_sample_point(i);
-    self->_contours.back()._points.push_back(LPoint2f(p[0], p[1]));
-  }
-  return 0;
+  PT(NurbsCurveResult) ncr = nce.evaluate();
+  return self->outline_nurbs(ncr);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -831,7 +944,6 @@ outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
                  const FT_Vector *to, void *user) {
   DynamicTextFont *self = (DynamicTextFont *)user;
   nassertr(!self->_contours.empty(), 1);
-  const LPoint2f &q = self->_contours.back()._points.back();
 
   // Convert from 26.6 pixel units to Panda units.
   float scale = 1.0f / (64.0f * self->_font_pixels_per_unit);
@@ -845,22 +957,69 @@ outline_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
   nce.local_object();
   nce.set_order(4);
   nce.reset(4);
-  nce.set_vertex(0, LVecBase3f(q[0], q[1], 0.0f));
+  nce.set_vertex(0, LVecBase3f(self->_q[0], self->_q[1], 0.0f));
   nce.set_vertex(1, LVecBase3f(c1[0], c1[1], 0.0f));
   nce.set_vertex(2, LVecBase3f(c2[0], c2[1], 0.0f));
   nce.set_vertex(3, LVecBase3f(p[0], p[1], 0.0f));
 
+  self->_q = p;
+
   PT(NurbsCurveResult) ncr = nce.evaluate();
+  return self->outline_nurbs(ncr);
+}
 
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::outline_nurbs
+//       Access: Private
+//  Description: Called internally by outline_cubic_to() and
+//               outline_conic_to().
+////////////////////////////////////////////////////////////////////
+int DynamicTextFont::
+outline_nurbs(NurbsCurveResult *ncr) {
   // Sample it down so that the lines approximate the curve to within
   // a "pixel."
-  ncr->adaptive_sample(1.0f / self->_font_pixels_per_unit);
+  ncr->adaptive_sample(1.0f / _font_pixels_per_unit);
 
   int num_samples = ncr->get_num_samples();
-  for (int i = 1; i < num_samples; ++i) {
+
+  bool needs_connect = false;
+  int start = 1;
+  if (_contours.back()._points.empty()) {
+    // If we haven't got the first point of this contour yet, we must
+    // add it now.
+    start = 0;
+  } else {
+    needs_connect = true;
+  }
+
+  for (int i = start; i < num_samples; ++i) {
+    float st = ncr->get_sample_t(i);
     const LPoint3f &p = ncr->get_sample_point(i);
-    self->_contours.back()._points.push_back(LPoint2f(p[0], p[1]));
+
+    float st0 = st, st1 = st;
+    if (i > 0) {
+      st0 = ncr->get_sample_t(i - 1) * 0.1f + st * 0.9f;
+    }
+    if (i < num_samples - 1) {
+      st1 = ncr->get_sample_t(i + 1) * 0.1f + st * 0.9f;
+    }
+    // Compute the tangent by deltaing nearby points.  Don't evaluate
+    // the tangent from the NURBS, since that doesn't appear to be
+    // reliable.
+    LPoint3f p0, p1;
+    ncr->eval_point(st0, p0);
+    ncr->eval_point(st1, p1);
+    LVector3f t = p1 - p0;
+    t.normalize();
+
+    if (needs_connect) {
+      _contours.back()._points.back().connect_to(LVector2f(t[0], t[1]));
+      needs_connect = false;
+    }
+
+    _contours.back()._points.push_back(ContourPoint(p[0], p[1], t[0], t[1]));
   }
+
   return 0;
 }
 

+ 15 - 2
panda/src/text/dynamicTextFont.h

@@ -35,6 +35,8 @@
 #include <ft2build.h>
 #include FT_FREETYPE_H
 
+class NurbsCurveResult;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : DynamicTextFont
 // Description : A DynamicTextFont is a special TextFont object that
@@ -140,7 +142,7 @@ private:
   DynamicTextGlyph *slot_glyph(int character, int x_size, int y_size);
 
   void render_wireframe_contours(DynamicTextGlyph *glyph);
-  void render_polygon_contours(DynamicTextGlyph *glyph);
+  void render_polygon_contours(DynamicTextGlyph *glyph, bool face, bool extrude);
 
   static int outline_move_to(const FT_Vector *to, void *user);
   static int outline_line_to(const FT_Vector *to, void *user);
@@ -149,6 +151,7 @@ private:
   static int outline_cubic_to(const FT_Vector *control1, 
                               const FT_Vector *control2, 
                               const FT_Vector *to, void *user);
+  int outline_nurbs(NurbsCurveResult *ncr);
 
   int _texture_margin;
   float _poly_margin;
@@ -177,7 +180,16 @@ private:
   typedef pvector< PT(DynamicTextGlyph) > EmptyGlyphs;
   EmptyGlyphs _empty_glyphs;
 
-  typedef pvector<LPoint2f> Points;
+  class ContourPoint {
+  public:
+    INLINE ContourPoint(const LPoint2f &p, const LVector2f &in, 
+                        const LVector2f &out);
+    INLINE ContourPoint(float px, float py, float tx, float ty);
+    INLINE void connect_to(const LVector2f &out);
+    LPoint2f _p;
+    LVector2f _in, _out;  // tangents into and out of the vertex.
+  };
+  typedef pvector<ContourPoint> Points;
 
   class Contour {
   public:
@@ -188,6 +200,7 @@ private:
 
   typedef pvector<Contour> Contours;
   Contours _contours;
+  LPoint2f _q;  // The "current point".
 
 public:
   static TypeHandle get_class_type() {