DynamicTextFont::RenderMode

This commit is contained in:
David Rose 2007-01-24 00:27:39 +00:00
parent 7f225df16d
commit 629b215aee
16 changed files with 841 additions and 29 deletions

View File

@ -53,6 +53,18 @@ get_vertex(int n) const {
return _vertices[n];
}
////////////////////////////////////////////////////////////////////
// Function: Triangulator::is_left_winding
// Access: Published
// Description: Returns true if the polygon vertices are listed in
// counterclockwise order, or false if they appear to be
// listed in clockwise order.
////////////////////////////////////////////////////////////////////
INLINE bool Triangulator::
is_left_winding() const {
return check_left_winding(_polygon);
}
////////////////////////////////////////////////////////////////////
// Function: Triangulator::Triangle::Constructor
// Access: Private

View File

@ -72,9 +72,10 @@ clear_polygon() {
// This vertex should index into the vertex pool
// established by repeated calls to add_vertex().
//
// The vertices should be listed in counterclockwise
// order, and should not be repeated. In particular, do
// not repeat the first vertex at the end.
// The vertices may be listed in either clockwise or
// counterclockwise order. Vertices should not be
// repeated. In particular, do not repeat the first
// vertex at the end.
////////////////////////////////////////////////////////////////////
void Triangulator::
add_polygon_vertex(int index) {
@ -99,8 +100,9 @@ begin_hole() {
// This vertex should index into the vertex pool
// established by repeated calls to add_vertex().
//
// The vertices should be listed in clockwise order, and
// should not be repeated.
// The vertices may be listed in either clockwise or
// counterclockwise order. Vertices should not be
// repeated.
////////////////////////////////////////////////////////////////////
void Triangulator::
add_hole_vertex(int index) {
@ -123,11 +125,11 @@ triangulate() {
// Set up the list of segments.
seg.clear();
seg.push_back(segment_t()); // we don't use the first entry.
make_segment(_polygon);
make_segment(_polygon, true);
Holes::const_iterator hi;
for (hi = _holes.begin(); hi != _holes.end(); ++hi) {
make_segment(*hi);
make_segment(*hi, false);
}
// Shuffle the segment index.
@ -305,30 +307,75 @@ get_triangle_v2(int n) const {
#define LENGTH(v0) (sqrt((v0).x * (v0).x + (v0).y * (v0).y))
////////////////////////////////////////////////////////////////////
// Function: Triangulator::check_left_winding
// Access: Private
// Description: Returns true if the list of vertices is
// counter-clockwise, false if it is clockwise.
////////////////////////////////////////////////////////////////////
bool Triangulator::
check_left_winding(const vector_int &range) const {
// We do this by computing the polygon's signed area. If it comes
// out negative, the polygon is right-winding.
double area = 0.0;
size_t j = range.size() - 1;
for (size_t i = 0; i < range.size(); ++i) {
const LPoint2d &p0 = _vertices[range[j]];
const LPoint2d &p1 = _vertices[range[i]];
area += p0[0] * p1[1] - p0[1] * p1[0];
j = i;
}
return area >= 0.0;
}
////////////////////////////////////////////////////////////////////
// Function: Triangulator::make_segment
// Access: Private
// Description: Converts a linear list of integer vertices to a list
// of segment_t.
// of segment_t. If want_left_winding is true, the list
// is reversed if necessary to make it left-winding;
// otherwise, it is reversed to make it right-winding.
////////////////////////////////////////////////////////////////////
void Triangulator::
make_segment(const vector_int &range) {
make_segment(const vector_int &range, bool want_left_winding) {
int num_points = (int)range.size();
nassertv(num_points >= 2);
int first = (int)seg.size();
int last = first + num_points - 1;
seg.push_back(segment_t(this, range[0], range[1],
last, first + 1));
if (want_left_winding == check_left_winding(range)) {
// Keep it in its natural order.
int first = (int)seg.size();
int last = first + num_points - 1;
seg.push_back(segment_t(this, range[0], range[1],
last, first + 1));
for (int i = 1; i < num_points - 1; ++i) {
seg.push_back(segment_t(this, range[i], range[i + 1],
first + i - 1, first + i + 1));
}
seg.push_back(segment_t(this, range[num_points - 1], range[0],
last - 1, first));
} else {
// Reverse it.
seg.push_back(segment_t(this, range[0], range[num_points - 1],
last, first + 1));
for (int i = 1; i < num_points - 1; ++i) {
seg.push_back(segment_t(this, range[num_points - i], range[num_points - i - 1],
first + i - 1, first + i + 1));
}
seg.push_back(segment_t(this, range[1], range[0],
last - 1, first));
for (int i = 1; i < num_points - 1; ++i) {
seg.push_back(segment_t(this, range[i], range[i + 1],
first + i - 1, first + i + 1));
}
seg.push_back(segment_t(this, range[num_points - 1], range[0],
last - 1, first));
}
/* Return the next segment in the generated random ordering of all the */

View File

@ -52,6 +52,7 @@ PUBLISHED:
void clear_polygon();
void add_polygon_vertex(int index);
INLINE bool is_left_winding() const;
void begin_hole();
void add_hole_vertex(int index);
@ -170,7 +171,8 @@ private:
vector_int visited;
void make_segment(const vector_int &range);
bool check_left_winding(const vector_int &range) const;
void make_segment(const vector_int &range, bool want_left_winding);
int choose_segment();
static double math_log2(double v);

View File

@ -28,7 +28,7 @@ INLINE NurbsCurveResult::
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_start_t
// Access: Public
// Access: Published
// Description: Returns the first legal value of t on the curve.
// Usually this is 0.0.
////////////////////////////////////////////////////////////////////
@ -39,7 +39,7 @@ get_start_t() const {
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_end_t
// Access: Public
// Access: Published
// Description: Returns the last legal value of t on the curve.
////////////////////////////////////////////////////////////////////
INLINE float NurbsCurveResult::
@ -124,7 +124,7 @@ eval_extended_points(float t, int d, float result[], int num_values) {
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_num_segments
// Access: Public
// Access: Published
// Description: Returns the number of piecewise continuous segments
// within the curve. This number is usually not
// important unless you plan to call
@ -137,7 +137,7 @@ get_num_segments() const {
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_segment_t
// Access: Public
// Access: Published
// Description: Accepts a t value in the range [0, 1], and assumed to
// be relative to the indicated segment (as in
// eval_segment_point()), and returns the corresponding
@ -147,3 +147,59 @@ INLINE float NurbsCurveResult::
get_segment_t(int segment, float t) const {
return t * (_basis.get_to(segment) - _basis.get_from(segment)) + _basis.get_from(segment);
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_num_samples
// Access: Published
// Description: Returns the number of sample points generated by the
// previous call to adaptive_sample().
////////////////////////////////////////////////////////////////////
INLINE int NurbsCurveResult::
get_num_samples() const {
return (int)_adaptive_result.size();
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_sample_t
// Access: Published
// Description: Returns the t value of the nth sample point generated
// by the previous call to adaptive_sample().
////////////////////////////////////////////////////////////////////
INLINE float NurbsCurveResult::
get_sample_t(int n) const {
nassertr(n >= 0 && n < (int)_adaptive_result.size(), 0.0f);
return _adaptive_result[n]._t;
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_sample_point
// Access: Published
// Description: Returns the point on the curve of the nth sample
// point generated by the previous call to
// adaptive_sample().
//
// For tangents, or extended points, you should use
// get_sample_t() and pass it into eval_tangent() or
// eval_extended_point().
////////////////////////////////////////////////////////////////////
INLINE const LPoint3f &NurbsCurveResult::
get_sample_point(int n) const {
nassertr(n >= 0 && n < (int)_adaptive_result.size(), LPoint3f::zero());
return _adaptive_result[n]._point;
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::get_segment_t
// Access: Public
// Description: Accepts a t value in the range [0, 1], and assumed to
// be relative to the indicated segment (as in
// eval_segment_point()), and returns the corresponding
// t value in the entire curve (as in eval_point()).
////////////////////////////////////////////////////////////////////
INLINE NurbsCurveResult::AdaptiveSample::
AdaptiveSample(float t, const LPoint3f &point) :
_t(t),
_point(point)
{
}

View File

@ -199,6 +199,41 @@ eval_segment_extended_points(int segment, float t, int d,
}
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::adaptive_sample
// Access: Published
// Description: Determines the set of subdivisions necessary to
// approximate the curve with a set of linear segments,
// no point of which is farther than tolerance units
// from the actual curve.
//
// After this call, you may walk through the resulting
// set of samples with get_num_samples(),
// get_sample_t(), and get_sample_point().
////////////////////////////////////////////////////////////////////
void NurbsCurveResult::
adaptive_sample(float tolerance) {
float tolerance_2 = tolerance * tolerance;
_adaptive_result.clear();
LPoint3f p0, p1;
int num_segments = _basis.get_num_segments();
for (int segment = 0; segment < num_segments; ++segment) {
eval_segment_point(segment, 0.0f, p0);
if (segment == 0 || !p0.almost_equal(p1)) {
// We explicitly push the first point, and the boundary point
// anytime the segment boundary is discontinuous.
_adaptive_result.push_back(AdaptiveSample(_basis.get_from(segment), p0));
}
eval_segment_point(segment, 1.0f, p1);
// Then we recusrively get the remaining points in the segment.
r_adaptive_sample(segment, 0.0f, p0, 1.0f, p1, tolerance_2);
}
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::find_segment
// Access: Private
@ -264,3 +299,46 @@ r_find_segment(float t, int top, int bot) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::r_adaptive_sample
// Access: Private
// Description: Recursively subdivides a potential evaluation of the
// segment until it is found to be within tolerance.
// This will add everything up to and including t1, but
// excluding t0.
////////////////////////////////////////////////////////////////////
void NurbsCurveResult::
r_adaptive_sample(int segment, float t0, const LPoint3f &p0,
float t1, const LPoint3f &p1, float tolerance_2) {
float tmid = (t0 + t1) * 0.5f;
LPoint3f pmid;
eval_segment_point(segment, tmid, pmid);
if (sqr_dist_to_line(pmid, p0, p1 - p0) > tolerance_2) {
// The line is still too curved--subdivide.
r_adaptive_sample(segment, t0, p0, tmid, pmid, tolerance_2);
r_adaptive_sample(segment, tmid, pmid, t1, p1, tolerance_2);
} else {
// The line is sufficiently close. Stop here.
_adaptive_result.push_back(AdaptiveSample(_basis.scale_t(segment, t1), p1));
}
}
////////////////////////////////////////////////////////////////////
// Function: NurbsCurveResult::sqr_dist_to_line
// Access: Private, Static
// Description: A support function for r_adaptive_sample(), this
// computes the minimum distance from a point to a line,
// and returns the distance squared.
////////////////////////////////////////////////////////////////////
float NurbsCurveResult::
sqr_dist_to_line(const LPoint3f &point, const LPoint3f &origin,
const LVector3f &vec) {
LVector3f norm = vec;
norm.normalize();
LVector3f d = point - origin;
float hyp_2 = d.length_squared();
float leg = d.dot(norm);
return hyp_2 - leg * leg;
}

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "referenceCount.h"
#include "nurbsBasisVector.h"
#include "vector_float.h"
class NurbsVertex;
@ -63,11 +64,21 @@ PUBLISHED:
void eval_segment_extended_points(int segment, float t, int d,
float result[], int num_values) const;
INLINE float get_segment_t(int segment, float t) const;
void adaptive_sample(float tolerance);
INLINE int get_num_samples() const;
INLINE float get_sample_t(int n) const;
INLINE const LPoint3f &get_sample_point(int n) const;
private:
int find_segment(float t);
int r_find_segment(float t, int top, int bot) const;
void r_adaptive_sample(int segment, float t0, const LPoint3f &p0,
float t1, const LPoint3f &p1, float tolerance_2);
static float sqr_dist_to_line(const LPoint3f &point, const LPoint3f &origin,
const LVector3f &vec);
NurbsBasisVector _basis;
const NurbsVertex *_verts;
@ -77,10 +88,18 @@ private:
typedef pvector<LMatrix4f> ComposedGeom;
ComposedGeom _composed;
int _last_segment;
float _last_from;
float _last_to;
class AdaptiveSample {
public:
INLINE AdaptiveSample(float t, const LPoint3f &point);
float _t;
LPoint3f _point;
};
typedef pvector<AdaptiveSample> AdaptiveResult;
AdaptiveResult _adaptive_result;
};
#include "nurbsCurveResult.I"

View File

@ -170,12 +170,18 @@ unload_font() {
// false otherwise.
////////////////////////////////////////////////////////////////////
bool FreetypeFont::
load_glyph(int glyph_index) {
load_glyph(int glyph_index, bool prerender) {
int flags = FT_LOAD_RENDER;
if (!_native_antialias) {
flags |= FT_LOAD_MONOCHROME;
}
if (!prerender) {
// If we want to render as an outline font, don't pre-render it to
// a bitmap.
flags = 0;
}
int error = FT_Load_Glyph(_face, glyph_index, flags);
if (error) {
pnmtext_cat.error()

View File

@ -76,7 +76,7 @@ public:
INLINE static float get_points_per_inch();
protected:
bool load_glyph(int glyph_index);
bool load_glyph(int glyph_index, bool prerender = true);
void copy_bitmap_to_pnmimage(const FT_Bitmap &bitmap, PNMImage &image);
private:
@ -99,7 +99,7 @@ protected:
FT_Face _face;
private:
protected:
bool _font_loaded;
// This string is used to hold the data read from the font file in

View File

@ -7,7 +7,8 @@
#define TARGET text
#define LOCAL_LIBS \
putil gobj pgraph linmath \
pnmtext pnmimage gsgbase mathutil
pnmtext pnmimage gsgbase mathutil \
parametrics
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx

View File

@ -182,6 +182,10 @@ ConfigVariableEnum<Texture::WrapMode> text_wrap_mode
("text-wrap-mode", Texture::WM_border_color,
PRC_DESC("The default wrap mode for dynamic text fonts"));
ConfigVariableEnum<DynamicTextFont::RenderMode> text_render_mode
("text-render-mode", DynamicTextFont::RM_texture,
PRC_DESC("The default render mode for dynamic text fonts"));
////////////////////////////////////////////////////////////////////

View File

@ -102,7 +102,8 @@ get_pixels_per_unit() const {
// larger than 1.0 to improve the font's antialiasing
// (since FreeType doesn't really do a swell job of
// antialiasing by itself). There is some performance
// implication for setting this different than 1.0.
// implication for setting this different than 1.0, but
// it is probably small.
//
// This should only be called before any characters have
// been requested out of the font, or immediately after
@ -360,6 +361,54 @@ get_anisotropic_degree() const {
return _anisotropic_degree;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::set_render_mode
// Access: Published
// Description: Specifies the way the glyphs on this particular font
// are generated. The default is RM_texture, which is
// the only mode supported for bitmap fonts. Other modes
// are possible for most modern fonts.
////////////////////////////////////////////////////////////////////
INLINE void DynamicTextFont::
set_render_mode(DynamicTextFont::RenderMode render_mode) {
_render_mode = render_mode;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::get_render_mode
// Access: Published
// Description: Returns the way the glyphs on this particular font
// are generated. See set_render_mode().
////////////////////////////////////////////////////////////////////
INLINE DynamicTextFont::RenderMode DynamicTextFont::
get_render_mode() const {
return _render_mode;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::set_winding_order
// Access: Published
// Description: Specifies an explicitly winding order on this
// particular font. This is only necessary if the
// render_mode is RM_polygon or RM_solid, and only if
// FreeType appears to guess wrong on this font.
// Normally, you should leave this at WO_default.
////////////////////////////////////////////////////////////////////
INLINE void DynamicTextFont::
set_winding_order(DynamicTextFont::WindingOrder winding_order) {
_winding_order = winding_order;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::get_winding_order
// Access: Published
// Description: Returns the winding order set via set_winding_order().
////////////////////////////////////////////////////////////////////
INLINE DynamicTextFont::WindingOrder DynamicTextFont::
get_winding_order() const {
return _winding_order;
}
INLINE ostream &
operator << (ostream &out, const DynamicTextFont &dtf) {
return out << dtf.get_name();

View File

@ -20,10 +20,29 @@
#ifdef HAVE_FREETYPE
#undef interface // I don't know where this symbol is defined, but it interferes with FreeType.
#include FT_OUTLINE_H
#include FT_STROKER_H
#include FT_BBOX_H
#ifdef FT_BITMAP_H
#include FT_BITMAP_H
#endif
#include "config_text.h"
#include "config_util.h"
#include "config_express.h"
#include "virtualFileSystem.h"
#include "geomVertexData.h"
#include "geomVertexFormat.h"
#include "geomVertexWriter.h"
#include "geomLinestrips.h"
#include "geomTriangles.h"
#include "renderState.h"
#include "string_utils.h"
#include "triangulator.h"
#include "nurbsCurveEvaluator.h"
#include "nurbsCurveResult.h"
//#include "renderModeAttrib.h"
TypeHandle DynamicTextFont::_type_handle;
@ -229,6 +248,51 @@ get_glyph(int character, const TextGlyph *&glyph) {
return (glyph_index != 0 && glyph != (DynamicTextGlyph *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::string_render_mode
// Access: Public
// Description: Returns the RenderMode value associated with the given
// string representation, or RM_invalid if the string
// does not match any known RenderMode value.
////////////////////////////////////////////////////////////////////
DynamicTextFont::RenderMode DynamicTextFont::
string_render_mode(const string &string) {
if (cmp_nocase_uh(string, "texture") == 0) {
return RM_texture;
} else if (cmp_nocase_uh(string, "wireframe") == 0) {
return RM_wireframe;
} else if (cmp_nocase_uh(string, "polygon") == 0) {
return RM_polygon;
} else if (cmp_nocase_uh(string, "extruded") == 0) {
return RM_extruded;
} else if (cmp_nocase_uh(string, "solid") == 0) {
return RM_solid;
} else {
return RM_invalid;
}
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::string_winding_order
// Access: Public
// Description: Returns the WindingOrder value associated with the given
// string representation, or WO_invalid if the string
// does not match any known WindingOrder value.
////////////////////////////////////////////////////////////////////
DynamicTextFont::WindingOrder DynamicTextFont::
string_winding_order(const string &string) {
if (cmp_nocase_uh(string, "default") == 0) {
return WO_default;
} else if (cmp_nocase_uh(string, "left") == 0) {
return WO_left;
} else if (cmp_nocase_uh(string, "right") == 0) {
return WO_right;
} else {
return WO_invalid;
}
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::initialize
// Access: Private
@ -253,6 +317,9 @@ initialize() {
// require generating mipmaps, but does require hardware support.
_anisotropic_degree = text_anisotropic_degree;
_render_mode = text_render_mode;
_winding_order = WO_default;
_preferred_page = 0;
}
@ -284,7 +351,7 @@ update_filters() {
////////////////////////////////////////////////////////////////////
DynamicTextGlyph *DynamicTextFont::
make_glyph(int character, int glyph_index) {
if (!load_glyph(glyph_index)) {
if (!load_glyph(glyph_index, false)) {
return (DynamicTextGlyph *)NULL;
}
@ -293,6 +360,79 @@ make_glyph(int character, int glyph_index) {
float advance = slot->advance.x / 64.0;
if (_render_mode != RM_texture &&
slot->format == ft_glyph_format_outline) {
// Re-stroke the glyph to make it an outline glyph.
/*
FT_Stroker stroker;
FT_Stroker_New(_face->memory, &stroker);
FT_Stroker_Set(stroker, 16 * 16, FT_STROKER_LINECAP_BUTT,
FT_STROKER_LINEJOIN_ROUND, 0);
FT_Stroker_ParseOutline(stroker, &slot->outline, 0);
FT_UInt num_points, num_contours;
FT_Stroker_GetCounts(stroker, &num_points, &num_contours);
FT_Outline border;
FT_Outline_New(_ft_library, num_points, num_contours, &border);
border.n_points = 0;
border.n_contours = 0;
FT_Stroker_Export(stroker, &border);
FT_Stroker_Done(stroker);
FT_Outline_Done(_ft_library, &slot->outline);
memcpy(&slot->outline, &border, sizeof(border));
*/
// Ask FreeType to extract the contours out of the outline
// description.
FT_Outline_Funcs funcs;
memset(&funcs, 0, sizeof(funcs));
funcs.move_to = (FT_Outline_MoveTo_Func)outline_move_to;
funcs.line_to = (FT_Outline_LineTo_Func)outline_line_to;
funcs.conic_to = (FT_Outline_ConicTo_Func)outline_conic_to;
funcs.cubic_to = (FT_Outline_CubicTo_Func)outline_cubic_to;
WindingOrder wo = _winding_order;
if (wo == WO_default) {
// If we weren't told an explicit winding order, ask FreeType to
// figure it out. Sometimes it appears to guess wrong.
if (FT_Outline_Get_Orientation(&slot->outline) == FT_ORIENTATION_FILL_RIGHT) {
wo = WO_right;
} else {
wo = WO_left;
}
}
if (wo != WO_left) {
FT_Outline_Reverse(&slot->outline);
}
_contours.clear();
FT_Outline_Decompose(&slot->outline, &funcs, (void *)this);
PT(DynamicTextGlyph) glyph =
new DynamicTextGlyph(character, advance / _font_pixels_per_unit);
switch (_render_mode) {
case RM_wireframe:
render_wireframe_contours(glyph);
return glyph;
case RM_polygon:
render_polygon_contours(glyph);
return glyph;
default:
break;
}
}
// Render the glyph if necessary.
if (slot->format != ft_glyph_format_bitmap) {
FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
}
if (bitmap.width == 0 || bitmap.rows == 0) {
// If we got an empty bitmap, it's a special case.
PT(DynamicTextGlyph) glyph =
@ -485,4 +625,305 @@ slot_glyph(int character, int x_size, int y_size) {
}
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::render_wireframe_contours
// Access: Private
// Description: Converts from the _contours list to an actual glyph
// geometry, as a wireframe render.
////////////////////////////////////////////////////////////////////
void DynamicTextFont::
render_wireframe_contours(DynamicTextGlyph *glyph) {
PT(GeomVertexData) vdata = new GeomVertexData
(string(), GeomVertexFormat::get_v3(),
Geom::UH_static);
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
Contours::const_iterator ci;
for (ci = _contours.begin(); ci != _contours.end(); ++ci) {
const Contour &contour = (*ci);
Points::const_iterator pi;
for (pi = contour._points.begin(); pi != contour._points.end(); ++pi) {
const LPoint2f &p = (*pi);
vertex.add_data3f(p[0], 0.0f, p[1]);
}
lines->add_next_vertices(contour._points.size());
lines->close_primitive();
}
glyph->set_geom(vdata, lines, RenderState::make_empty());
_contours.clear();
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::render_polygon_contours
// Access: Private
// Description: Converts from the _contours list to an actual glyph
// geometry, as a polygon render.
////////////////////////////////////////////////////////////////////
void DynamicTextFont::
render_polygon_contours(DynamicTextGlyph *glyph) {
PT(GeomVertexData) vdata = new GeomVertexData
(string(), GeomVertexFormat::get_v3(),
Geom::UH_static);
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
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);
}
contour._is_solid = t.is_left_winding();
}
// 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);
}
// 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();
}
}
}
glyph->set_geom(vdata, tris, RenderState::make_empty());
// glyph->set_geom(vdata, tris, RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_wireframe)));
_contours.clear();
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::outline_move_to
// Access: Private, Static
// Description: A callback from FT_Outline_Decompose(). It marks the
// beginning of a new contour.
////////////////////////////////////////////////////////////////////
int DynamicTextFont::
outline_move_to(const FT_Vector *to, void *user) {
DynamicTextFont *self = (DynamicTextFont *)user;
// Convert from 26.6 pixel units to Panda units.
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);
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::outline_line_to
// Access: Private, Static
// Description: A callback from FT_Outline_Decompose(). It marks a
// straight line in the contour.
////////////////////////////////////////////////////////////////////
int DynamicTextFont::
outline_line_to(const FT_Vector *to, void *user) {
DynamicTextFont *self = (DynamicTextFont *)user;
nassertr(!self->_contours.empty(), 1);
// Convert from 26.6 pixel units to Panda units.
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);
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::outline_conic_to
// Access: Private, Static
// Description: A callback from FT_Outline_Decompose(). It marks a
// parabolic (3rd-order) Bezier curve in the contour.
////////////////////////////////////////////////////////////////////
int DynamicTextFont::
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);
LPoint2f c = LPoint2f(control->x, control->y) * scale;
LPoint2f p = LPoint2f(to->x, to->y) * scale;
// The NurbsCurveEvaluator will evaluate the Bezier segment for us.
NurbsCurveEvaluator nce;
nce.local_object();
nce.set_order(3);
nce.reset(3);
nce.set_vertex(0, LVecBase3f(q[0], 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);
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;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::outline_cubic_to
// Access: Private, Static
// Description: A callback from FT_Outline_Decompose(). It marks a
// cubic (4th-order) Bezier curve in the contour.
////////////////////////////////////////////////////////////////////
int DynamicTextFont::
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);
LPoint2f c1 = LPoint2f(control1->x, control1->y) * scale;
LPoint2f c2 = LPoint2f(control2->x, control2->y) * scale;
LPoint2f p = LPoint2f(to->x, to->y) * scale;
// The NurbsCurveEvaluator will evaluate the Bezier segment for us.
NurbsCurveEvaluator nce;
nce.local_object();
nce.set_order(4);
nce.reset(4);
nce.set_vertex(0, LVecBase3f(q[0], 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();
// 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;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::RenderMode output operator
// Description:
////////////////////////////////////////////////////////////////////
ostream &
operator << (ostream &out, DynamicTextFont::RenderMode rm) {
switch (rm) {
case DynamicTextFont::RM_texture:
return out << "texture";
case DynamicTextFont::RM_wireframe:
return out << "wireframe";
case DynamicTextFont::RM_polygon:
return out << "polygon";
case DynamicTextFont::RM_extruded:
return out << "extruded";
case DynamicTextFont::RM_solid:
return out << "solid";
case DynamicTextFont::RM_invalid:
return out << "invalid";
}
return out << "(**invalid DynamicTextFont::RenderMode(" << (int)rm << ")**)";
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::RenderMode input operator
// Description:
////////////////////////////////////////////////////////////////////
istream &
operator >> (istream &in, DynamicTextFont::RenderMode &rm) {
string word;
in >> word;
rm = DynamicTextFont::string_render_mode(word);
return in;
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::WindingOrder output operator
// Description:
////////////////////////////////////////////////////////////////////
ostream &
operator << (ostream &out, DynamicTextFont::WindingOrder wo) {
switch (wo) {
case DynamicTextFont::WO_default:
return out << "default";
case DynamicTextFont::WO_left:
return out << "left";
case DynamicTextFont::WO_right:
return out << "right";
case DynamicTextFont::WO_invalid:
return out << "invalid";
}
return out << "(**invalid DynamicTextFont::WindingOrder(" << (int)wo << ")**)";
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextFont::WindingOrder input operator
// Description:
////////////////////////////////////////////////////////////////////
istream &
operator >> (istream &in, DynamicTextFont::WindingOrder &wo) {
string word;
in >> word;
wo = DynamicTextFont::string_winding_order(word);
return in;
}
#endif // HAVE_FREETYPE

View File

@ -45,6 +45,34 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA DynamicTextFont : public TextFont, public FreetypeFont {
PUBLISHED:
enum RenderMode {
// Each glyph is a single textured rectangle
RM_texture,
// Each glyph is a lot of line segments
RM_wireframe,
// Each glyph is a lot of triangles
RM_polygon,
// a 3-D outline, like a cookie cutter
RM_extruded,
// combination of RM_extruded and RM_polygon
RM_solid,
// Returned by string_render_mode() for an invalid match.
RM_invalid,
};
enum WindingOrder {
WO_default,
WO_left,
WO_right,
WO_invalid,
};
DynamicTextFont(const Filename &font_filename, int face_index = 0);
DynamicTextFont(const char *font_data, int data_length, int face_index);
virtual ~DynamicTextFont();
@ -84,6 +112,11 @@ PUBLISHED:
INLINE void set_anisotropic_degree(int anisotropic_degree);
INLINE int get_anisotropic_degree() const;
INLINE void set_render_mode(RenderMode render_mode);
INLINE RenderMode get_render_mode() const;
INLINE void set_winding_order(WindingOrder winding_order);
INLINE WindingOrder get_winding_order() const;
int get_num_pages() const;
DynamicTextPage *get_page(int n) const;
@ -95,6 +128,9 @@ PUBLISHED:
public:
virtual bool get_glyph(int character, const TextGlyph *&glyph);
static RenderMode string_render_mode(const string &string);
static WindingOrder string_winding_order(const string &string);
private:
void initialize();
void update_filters();
@ -103,6 +139,17 @@ private:
void copy_pnmimage_to_texture(const PNMImage &image, DynamicTextGlyph *glyph);
DynamicTextGlyph *slot_glyph(int character, int x_size, int y_size);
void render_wireframe_contours(DynamicTextGlyph *glyph);
void render_polygon_contours(DynamicTextGlyph *glyph);
static int outline_move_to(const FT_Vector *to, void *user);
static int outline_line_to(const FT_Vector *to, void *user);
static int outline_conic_to(const FT_Vector *control,
const FT_Vector *to, void *user);
static int outline_cubic_to(const FT_Vector *control1,
const FT_Vector *control2,
const FT_Vector *to, void *user);
int _texture_margin;
float _poly_margin;
int _page_x_size, _page_y_size;
@ -111,6 +158,9 @@ private:
Texture::FilterType _magfilter;
int _anisotropic_degree;
RenderMode _render_mode;
WindingOrder _winding_order;
typedef pvector< PT(DynamicTextPage) > Pages;
Pages _pages;
int _preferred_page;
@ -127,6 +177,18 @@ private:
typedef pvector< PT(DynamicTextGlyph) > EmptyGlyphs;
EmptyGlyphs _empty_glyphs;
typedef pvector<LPoint2f> Points;
class Contour {
public:
Points _points;
bool _is_solid;
int _start_vertex;
};
typedef pvector<Contour> Contours;
Contours _contours;
public:
static TypeHandle get_class_type() {
return _type_handle;
@ -149,6 +211,11 @@ private:
INLINE ostream &operator << (ostream &out, const DynamicTextFont &dtf);
EXPCL_PANDA ostream &operator << (ostream &out, DynamicTextFont::RenderMode rm);
EXPCL_PANDA istream &operator >> (istream &in, DynamicTextFont::RenderMode &rm);
EXPCL_PANDA ostream &operator << (ostream &out, DynamicTextFont::WindingOrder wo);
EXPCL_PANDA istream &operator >> (istream &in, DynamicTextFont::WindingOrder &wo);
#include "dynamicTextFont.I"
#endif // HAVE_FREETYPE

View File

@ -168,4 +168,30 @@ make_geom(int bitmap_top, int bitmap_left, float advance, float poly_margin,
}
////////////////////////////////////////////////////////////////////
// Function: DynamicTextGlyph::set_geom
// Access: Public
// Description: Sets the geom from a pre-built object.
////////////////////////////////////////////////////////////////////
void DynamicTextGlyph::
set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state) {
// This function is called when _geom_count = 1, because it was
// constructed via the empty Glyph constructor.
nassertv(_geom_count == 1);
_geom_count--;
PT(Geom) geom = new GeomTextGlyph(this, vdata);
geom->add_primitive(prim);
_geom = geom;
// The above will increment our _geom_count to 1. Reset it back
// down to 0, since our own internal Geom doesn't count.
nassertv(_geom_count == 1);
_geom_count--;
_state = state;
}
#endif // HAVE_FREETYPE

View File

@ -53,6 +53,8 @@ public:
void make_geom(int top, int left, float advance, float poly_margin,
float tex_x_size, float tex_y_size,
float font_pixels_per_unit, float tex_pixels_per_unit);
void set_geom(GeomVertexData *vdata, GeomPrimitive *prim,
const RenderState *state);
DynamicTextPage *_page;
int _geom_count;

View File

@ -289,6 +289,8 @@ load_default_font() {
// Loading the compiled-in FreeType font is relatively easy.
_default_font = new DynamicTextFont((const char *)default_font_data,
default_font_size, 0);
// The compiled-in font seems to confuse FreeType about its winding order.
((DynamicTextFont *)_default_font.p())->set_winding_order(DynamicTextFont::WO_left);
#else
// The compiled-in Bam font requires creating a BamFile object to