From 91f22c514a16bbde5fad38e22b11b5b23850db10 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 30 Jan 2007 11:50:38 +0000 Subject: [PATCH] more geometric font support --- panda/src/text/dynamicTextFont.I | 44 +++++ panda/src/text/dynamicTextFont.cxx | 287 ++++++++++++++++++++++------- panda/src/text/dynamicTextFont.h | 17 +- 3 files changed, 282 insertions(+), 66 deletions(-) diff --git a/panda/src/text/dynamicTextFont.I b/panda/src/text/dynamicTextFont.I index f410cc3378..c35efde180 100644 --- a/panda/src/text/dynamicTextFont.I +++ b/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(); diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index 389a54a2fc..290723a259 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/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; - // 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); - } + 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); + } + } + } + + 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)); + self->_q = p; + 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); - - 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; + 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)); - PT(NurbsCurveResult) ncr = nce.evaluate(); + 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) { - const LPoint3f &p = ncr->get_sample_point(i); - self->_contours.back()._points.push_back(LPoint2f(p[0], p[1])); + + 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); + + 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; } diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h index bf11e8739c..e0ceb1b6ad 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -35,6 +35,8 @@ #include #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 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 Points; class Contour { public: @@ -188,6 +200,7 @@ private: typedef pvector Contours; Contours _contours; + LPoint2f _q; // The "current point". public: static TypeHandle get_class_type() {