Browse Source

initial pass at dynamic text

David Rose 24 years ago
parent
commit
9292457ce5

+ 14 - 1
panda/src/text/Sources.pp

@@ -2,6 +2,8 @@
                    dtoolutil:c dtoolbase:c dtool:m
                    dtoolutil:c dtoolbase:c dtool:m
 
 
 #begin lib_target
 #begin lib_target
+  #define USE_FREETYPE yes
+
   #define TARGET text
   #define TARGET text
   #define LOCAL_LIBS \
   #define LOCAL_LIBS \
     cull putil gobj sgattrib graph sgraph linmath sgraphutil pnmimage gsgbase \
     cull putil gobj sgattrib graph sgraph linmath sgraphutil pnmimage gsgbase \
@@ -11,16 +13,27 @@
 
 
   #define SOURCES \
   #define SOURCES \
     config_text.h \
     config_text.h \
+    dynamicTextFont.I dynamicTextFont.h \
+    dynamicTextGlyph.I dynamicTextGlyph.h \
+    dynamicTextPage.I dynamicTextPage.h \
     staticTextFont.I staticTextFont.h \
     staticTextFont.I staticTextFont.h \
     textFont.I textFont.h \
     textFont.I textFont.h \
     textGlyph.I textGlyph.h \
     textGlyph.I textGlyph.h \
     textNode.I textNode.h textNode.cxx
     textNode.I textNode.h textNode.cxx
 
 
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
-    config_text.cxx staticTextFont.cxx textFont.cxx textGlyph.cxx
+    config_text.cxx \
+    dynamicTextFont.cxx \
+    dynamicTextGlyph.cxx \
+    dynamicTextPage.cxx \
+    staticTextFont.cxx \
+    textFont.cxx textGlyph.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     config_text.h \
     config_text.h \
+    dynamicTextFont.I dynamicTextFont.h \
+    dynamicTextGlyph.I dynamicTextGlyph.h \
+    dynamicTextPage.I dynamicTextPage.h \
     staticTextFont.I staticTextFont.h \
     staticTextFont.I staticTextFont.h \
     textFont.I textFont.h \
     textFont.I textFont.h \
     textGlyph.I textGlyph.h \
     textGlyph.I textGlyph.h \

+ 4 - 0
panda/src/text/config_text.cxx

@@ -18,6 +18,8 @@
 
 
 #include "config_text.h"
 #include "config_text.h"
 #include "staticTextFont.h"
 #include "staticTextFont.h"
+#include "dynamicTextFont.h"
+#include "dynamicTextPage.h"
 #include "textFont.h"
 #include "textFont.h"
 #include "textNode.h"
 #include "textNode.h"
 
 
@@ -28,6 +30,8 @@ NotifyCategoryDef(text, "");
 
 
 ConfigureFn(config_text) {
 ConfigureFn(config_text) {
   StaticTextFont::init_type();
   StaticTextFont::init_type();
+  DynamicTextFont::init_type();
+  DynamicTextPage::init_type();
   TextFont::init_type();
   TextFont::init_type();
   TextNode::init_type();
   TextNode::init_type();
 }
 }

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

@@ -0,0 +1,76 @@
+// Filename: dynamicTextFont.I
+// Created by:  drose (08Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::set_margin
+//       Access: Published
+//  Description: Sets the number of pixels of padding that is added
+//               around the border of each glyph before adding it to
+//               the texture map.  This reduces the bleed in from
+//               neighboring glyphs in the texture map.
+////////////////////////////////////////////////////////////////////
+INLINE void DynamicTextFont::
+set_margin(int margin) {
+  _margin = margin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::get_margin
+//       Access: Published
+//  Description: Returns the number of pixels of padding that is added
+//               around the border of each glyph.  See set_margin().
+////////////////////////////////////////////////////////////////////
+INLINE int DynamicTextFont::
+get_margin() const {
+  return _margin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::set_page_size
+//       Access: Published
+//  Description: Sets the x, y size of the textures that are created
+//               for the DynamicTextFont.
+////////////////////////////////////////////////////////////////////
+INLINE void DynamicTextFont::
+set_page_size(int x_size, int y_size) {
+  _page_x_size = x_size;
+  _page_y_size = y_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::get_page_x_size
+//       Access: Published
+//  Description: Returns the x size of the textures that are created
+//               for the DynamicTextFont.  See set_page_size().
+////////////////////////////////////////////////////////////////////
+INLINE int DynamicTextFont::
+get_page_x_size() const {
+  return _page_x_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::get_page_y_size
+//       Access: Published
+//  Description: Returns the y size of the textures that are created
+//               for the DynamicTextFont.  See set_page_size().
+////////////////////////////////////////////////////////////////////
+INLINE int DynamicTextFont::
+get_page_y_size() const {
+  return _page_y_size;
+}

+ 284 - 0
panda/src/text/dynamicTextFont.cxx

@@ -0,0 +1,284 @@
+// Filename: dynamicTextFont.cxx
+// Created by:  drose (08Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dynamicTextFont.h"
+
+#ifdef HAVE_FREETYPE
+
+#include "config_text.h"
+#include "config_util.h"
+
+FT_Library DynamicTextFont::_ft_library;
+bool DynamicTextFont::_ft_initialized = false;
+bool DynamicTextFont::_ft_ok = false;
+
+TypeHandle DynamicTextFont::_type_handle;
+
+
+// This constant determines how big a particular point size font
+// appears.  By convention, 10 points is 1 foot high.
+static const float points_per_unit = 10.0f;
+
+// A universal convention.
+static const float points_per_inch = 72.0f;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::Constructor
+//       Access: Published
+//  Description: The constructor expects the name of some font file
+//               that FreeType can read, along with face_index,
+//               indicating which font within the file to load
+//               (usually 0), the point size of the font, and the
+//               resolution at which to generate the font.  
+//
+//               The choice of point size affects the apparent size of
+//               the generated characters (as well as the clarity),
+//               while the pixels_per_unit affects only the clarity.
+////////////////////////////////////////////////////////////////////
+DynamicTextFont::
+DynamicTextFont(const Filename &font_filename, int face_index,
+                float point_size, float pixels_per_unit) {
+  _margin = 2;
+  _page_x_size = 256;
+  _page_y_size = 256;
+  _pixels_per_unit = pixels_per_unit;
+
+  float units_per_inch = (points_per_inch / points_per_unit);
+  int dpi = (int)(_pixels_per_unit * units_per_inch);
+
+  if (!_ft_initialized) {
+    initialize_ft_library();
+  }
+  if (!_ft_ok) {
+    text_cat.error()
+      << "Unable to read font " << font_filename << ": FreeType library not available.\n";
+    return;
+  }
+
+  Filename path(font_filename);
+  if (!path.resolve_filename(get_model_path())) {
+    text_cat.error()
+      << "Unable to find font file " << font_filename << "\n";
+  } else {
+    string os_specific = path.to_os_specific();
+    
+    int error = FT_New_Face(_ft_library,
+                            os_specific.c_str(),
+                            face_index,
+                            &_face);
+    if (error == FT_Err_Unknown_File_Format) {
+      text_cat.error()
+        << "Unable to read font " << font_filename << ": unknown file format.\n";
+    } else if (error) {
+      text_cat.error()
+        << "Unable to read font " << font_filename << ": invalid.\n";
+
+    } else {
+      string name = _face->family_name;
+      name += " ";
+      name += _face->style_name;
+
+      _is_valid = true;
+      set_name(name);
+
+      text_cat.info()
+        << "Loaded font " << get_name() << "\n";
+
+      error = FT_Set_Char_Size(_face,
+                               (int)(point_size * 64), (int)(point_size * 64),
+                               dpi, dpi);
+      if (error) {
+        text_cat.warning()
+          << "Unable to set point size of " << get_name() 
+          << " to " << point_size << " at " << dpi << " dots per inch.\n";
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::get_num_pages
+//       Access: Published
+//  Description: Returns the number of pages associated with the font.
+//               Initially, the font has zero pages; when the first
+//               piece of text is rendered with the font, it will add
+//               additional pages as needed.  Each page is a Texture
+//               object that contains the images for each of the
+//               glyphs currently in use somewhere.
+////////////////////////////////////////////////////////////////////
+int DynamicTextFont::
+get_num_pages() const {
+  return _pages.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::get_page
+//       Access: Published
+//  Description: Returns the nth page associated with the font.
+//               Initially, the font has zero pages; when the first
+//               piece of text is rendered with the font, it will add
+//               additional pages as needed.  Each page is a Texture
+//               object that contains the images for each of the
+//               glyphs currently in use somewhere.
+////////////////////////////////////////////////////////////////////
+DynamicTextPage *DynamicTextFont::
+get_page(int n) const {
+  nassertr(n >= 0 && n < (int)_pages.size(), (DynamicTextPage *)NULL);
+  return _pages[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::write
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DynamicTextFont::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << "DynamicTextFont " << get_name() << ".\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::get_glyph
+//       Access: Public, Virtual
+//  Description: Returns the glyph associated with the given character
+//               code, or NULL if there is no such glyph.
+////////////////////////////////////////////////////////////////////
+const TextGlyph *DynamicTextFont::
+get_glyph(int character) {
+  Cache::iterator ci = _cache.find(character);
+  if (ci != _cache.end()) {
+    return (*ci).second;
+  }
+  DynamicTextGlyph *glyph = make_glyph(character);
+  _cache.insert(Cache::value_type(character, glyph));
+  return glyph;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::make_glyph
+//       Access: Private
+//  Description: Slots a space in the texture map for the new
+//               character and renders the glyph, returning the
+//               newly-created TextGlyph object, or NULL if the
+//               glyph cannot be created for some reason.
+////////////////////////////////////////////////////////////////////
+DynamicTextGlyph *DynamicTextFont::
+make_glyph(int character) {
+  int error = FT_Load_Char(_face, character, FT_LOAD_RENDER);
+  if (error) {
+    text_cat.error()
+      << "Unable to render character " << character << "\n";
+    return (DynamicTextGlyph *)NULL;
+  }
+
+  FT_GlyphSlot slot = _face->glyph;
+  FT_Bitmap &bitmap = slot->bitmap;
+
+  if (bitmap.pixel_mode != ft_pixel_mode_grays) {
+    text_cat.error()
+      << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
+    return (DynamicTextGlyph *)NULL;
+  }
+
+  if (bitmap.num_grays != 256) {
+    // We expect 256 levels of grayscale to come back from FreeType,
+    // since that's what we asked for.
+    text_cat.warning()
+      << "Expected 256 levels of gray, got " << bitmap.num_grays << "\n";
+  }
+
+  DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows);
+
+  // Now copy the rendered glyph into the texture.
+  unsigned char *buffer_row = bitmap.buffer;
+  for (int yi = 0; yi < bitmap.rows; yi++) {
+    unsigned char *texture_row = glyph->get_row(yi);
+    nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL);
+    memcpy(texture_row, buffer_row, bitmap.width);
+    buffer_row += bitmap.pitch;
+  }
+  glyph->_page->mark_dirty(Texture::DF_image);
+
+  float advance = slot->advance.x / 64.0;
+  glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance,
+                   _pixels_per_unit);
+  return glyph;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::slot_glyph
+//       Access: Private
+//  Description: Chooses a page that will have room for a glyph of the
+//               indicated size (after expanding the indicated size by
+//               the current margin).  Returns the newly-allocated
+//               glyph on the chosen page; the glyph has not been
+//               filled in yet except with its size.
+////////////////////////////////////////////////////////////////////
+DynamicTextGlyph *DynamicTextFont::
+slot_glyph(int x_size, int y_size) {
+  // Increase the indicated size by the current margin.
+  x_size += _margin * 2;
+  y_size += _margin * 2;
+
+  Pages::iterator pi;
+  for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
+    DynamicTextPage *page = (*pi);
+
+    DynamicTextGlyph *glyph = page->slot_glyph(x_size, y_size, _margin);
+    if (glyph != (DynamicTextGlyph *)NULL) {
+      return glyph;
+    }
+
+    if (page->is_empty()) {
+      // If we couldn't even put in on an empty page, we're screwed.
+      text_cat.error()
+        << "Glyph of size " << x_size << " by " << y_size
+        << " won't fit on an empty page.\n";
+      return (DynamicTextGlyph *)NULL;
+    }
+  }
+
+  // We need to make a new page.
+  PT(DynamicTextPage) page = new DynamicTextPage(this);
+  _pages.push_back(page);
+  return page->slot_glyph(x_size, y_size, _margin);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextFont::initialize_ft_library
+//       Access: Private, Static
+//  Description: Should be called exactly once to initialize the
+//               FreeType library.
+////////////////////////////////////////////////////////////////////
+void DynamicTextFont::
+initialize_ft_library() {
+  if (!_ft_initialized) {
+    int error = FT_Init_FreeType(&_ft_library);
+    _ft_initialized = true;
+    if (error) {
+      text_cat.error()
+        << "Unable to initialize FreeType; DynamicTextFonts will not load.\n";
+    } else {
+      _ft_ok = true;
+    }
+  }
+}
+
+#endif  // HAVE_FREETYPE

+ 111 - 0
panda/src/text/dynamicTextFont.h

@@ -0,0 +1,111 @@
+// Filename: dynamicTextFont.h
+// Created by:  drose (08Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DYNAMICTEXTFONT_H
+#define DYNAMICTEXTFONT_H
+
+#include "pandabase.h"
+
+#ifdef HAVE_FREETYPE
+
+#include "config_text.h"
+#include "textFont.h"
+#include "dynamicTextGlyph.h"
+#include "dynamicTextPage.h"
+#include "filename.h"
+#include "pvector.h"
+#include "pmap.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+////////////////////////////////////////////////////////////////////
+//       Class : DynamicTextFont
+// Description : A DynamicTextFont is a special TextFont object that
+//               rasterizes its glyphs from a standard font file
+//               (e.g. a TTF file) on the fly.  It requires the
+//               FreeType 2.0 library (or any higher,
+//               backward-compatible version).
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DynamicTextFont : public TextFont {
+PUBLISHED:
+  DynamicTextFont(const Filename &font_filename, int face_index,
+                  float point_size, float pixels_per_unit);
+
+  INLINE void set_margin(int margin);
+  INLINE int get_margin() const;
+
+  INLINE void set_page_size(int x_size, int y_size);
+  INLINE int get_page_x_size() const;
+  INLINE int get_page_y_size() const;
+
+  int get_num_pages() const;
+  DynamicTextPage *get_page(int n) const;
+
+  virtual void write(ostream &out, int indent_level) const;
+
+public:
+  virtual const TextGlyph *get_glyph(int character);
+
+private:
+  DynamicTextGlyph *make_glyph(int character);
+  DynamicTextGlyph *slot_glyph(int x_size, int y_size);
+
+  static void initialize_ft_library();
+
+  int _margin;
+  int _page_x_size, _page_y_size;
+  float _pixels_per_unit;
+
+  typedef pvector< PT(DynamicTextPage) > Pages;
+  Pages _pages;
+
+  typedef pmap<int, DynamicTextGlyph *> Cache;
+  Cache _cache;
+
+  FT_Face _face;
+
+  static FT_Library _ft_library;
+  static bool _ft_initialized;
+  static bool _ft_ok;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TextFont::init_type();
+    register_type(_type_handle, "DynamicTextFont",
+                  TextFont::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;
+
+  friend class TextNode;
+};
+
+#include "dynamicTextFont.I"
+
+#endif  // HAVE_FREETYPE
+
+#endif

+ 54 - 0
panda/src/text/dynamicTextGlyph.I

@@ -0,0 +1,54 @@
+// Filename: dynamicTextGlyph.I
+// Created by:  drose (09Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextGlyph::Constructor
+//       Access: Publiic
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE DynamicTextGlyph::
+DynamicTextGlyph(DynamicTextPage *page, int x, int y,
+                 int x_size, int y_size, int margin) :
+  _page(page),
+  _x(x), _y(y),
+  _x_size(x_size), _y_size(y_size),
+  _margin(margin)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextGlyph::intersects
+//       Access: Public
+//  Description: Returns true if the particular position this glyph
+//               has been assigned to overlaps the rectangle whose
+//               top left corner is at x, y and whose size is given by
+//               x_size, y_size, or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool DynamicTextGlyph::
+intersects(int x, int y, int x_size, int y_size) const {
+  int hright = x + x_size;
+  int hbot = y + y_size;
+
+  int mright = _x + _x_size;
+  int mbot = _y + _y_size;
+
+  return !(x >= mright || hright <= _x ||
+           y >= mbot || hbot <= _y);
+}

+ 116 - 0
panda/src/text/dynamicTextGlyph.cxx

@@ -0,0 +1,116 @@
+// Filename: dynamicTextGlyph.cxx
+// Created by:  drose (09Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dynamicTextGlyph.h"
+
+#ifdef HAVE_FREETYPE
+
+#include "dynamicTextPage.h"
+#include "geomTristrip.h"
+#include "textureTransition.h"
+#include "transparencyTransition.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextGlyph::get_row
+//       Access: Publiic
+//  Description: Returns a pointer to the first byte in the pixel
+//               buffer associated with the leftmost pixel in the
+//               indicated row, where 0 is the topmost row and _y_size
+//               - _margin * 2 - 1 is the bottommost row.
+////////////////////////////////////////////////////////////////////
+unsigned char *DynamicTextGlyph::
+get_row(int y) {
+  nassertr(y >= 0 && y < _y_size - _margin * 2, (unsigned char *)NULL);
+  nassertr(_page != (DynamicTextPage *)NULL, (unsigned char *)NULL);
+  nassertr(_page->_pbuffer != (PixelBuffer *)NULL, (unsigned char *)NULL);
+
+  // First, offset y by the glyph's start.
+  y += _y + _margin;
+  // Also, get the x start.
+  int x = _x + _margin;
+
+  // Invert y.
+  y = _page->_pbuffer->get_ysize() - 1 - y;
+
+  int offset = (y * _page->_pbuffer->get_xsize()) + x;
+  return _page->_pbuffer->_image + offset; 
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextGlyph::make_geom
+//       Access: Publiic
+//  Description: Creates the actual geometry for the glyph.  The
+//               parameters bitmap_top and bitmap_left are from
+//               FreeType, and indicate the position of the top left
+//               corner of the bitmap relative to the glyph's origin.
+//               The advance number represents the number of pixels
+//               the pen should be advanced after drawing this glyph.
+////////////////////////////////////////////////////////////////////
+void DynamicTextGlyph::
+make_geom(int bitmap_top, int bitmap_left, 
+          float advance, float pixels_per_unit) {
+  // Determine the corners of the rectangle in geometric units.
+  float top = (bitmap_top + _margin) / pixels_per_unit;
+  float left = (bitmap_left - _margin) / pixels_per_unit;
+  float bottom = (bitmap_top - _y_size - _margin) / pixels_per_unit;
+  float right = (bitmap_left + _x_size + _margin) / pixels_per_unit;
+
+  // And the corresponding corners in UV units.
+  float uv_top = 1.0f - (float)_y / _page->get_y_size();
+  float uv_left = (float)_x / _page->get_x_size();
+  float uv_bottom = 1.0f - (float)(_y + _y_size) / _page->get_y_size();
+  float uv_right = (float)(_x + _x_size) / _page->get_x_size();
+
+  // Create a corresponding tristrip.
+  _geom = new GeomTristrip;
+
+  PTA_Vertexf coords;
+  coords.push_back(Vertexf(left, 0, top));
+  coords.push_back(Vertexf(left, 0, bottom));
+  coords.push_back(Vertexf(right, 0, top));
+  coords.push_back(Vertexf(right, 0, bottom));
+
+  PTA_TexCoordf texcoords;
+  texcoords.push_back(TexCoordf(uv_left, uv_top));
+  texcoords.push_back(TexCoordf(uv_left, uv_bottom));
+  texcoords.push_back(TexCoordf(uv_right, uv_top));
+  texcoords.push_back(TexCoordf(uv_right, uv_bottom));
+
+  PTA_Colorf colors;
+  colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+
+  PTA_int lengths;
+  lengths.push_back(4);
+
+  _geom->set_coords(coords);
+  _geom->set_texcoords(texcoords, G_PER_VERTEX);
+  _geom->set_colors(colors, G_OVERALL);
+  _geom->set_lengths(lengths);
+  _geom->set_num_prims(1);
+
+  TextureTransition *tex = new TextureTransition(_page);
+  TransparencyTransition *trans = new TransparencyTransition(TransparencyProperty::M_alpha);
+
+  _trans.set_transition(tex);
+  _trans.set_transition(trans);
+
+  _advance = advance / pixels_per_unit;
+}
+
+
+#endif  // HAVE_FREETYPE

+ 57 - 0
panda/src/text/dynamicTextGlyph.h

@@ -0,0 +1,57 @@
+// Filename: dynamicTextGlyph.h
+// Created by:  drose (09Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DYNAMICTEXTGLYPH_H
+#define DYNAMICTEXTGLYPH_H
+
+#include "pandabase.h"
+
+#ifdef HAVE_FREETYPE
+
+#include "textGlyph.h"
+
+class DynamicTextPage;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DynamicTextGlyph
+// Description : A specialization on TextGlyph that is generated and
+//               stored by a DynamicTextFont.  This keeps some
+//               additional information, such as where the glyph
+//               appears on a texture map.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DynamicTextGlyph : public TextGlyph {
+public:
+  INLINE DynamicTextGlyph(DynamicTextPage *page, int x, int y,
+                          int x_size, int y_size, int margin);
+
+  INLINE bool intersects(int x, int y, int x_size, int y_size) const;
+  unsigned char *get_row(int y);
+  void make_geom(int top, int left, float advance, float pixels_per_unit);
+
+  DynamicTextPage *_page;
+
+  int _x, _y;
+  int _x_size, _y_size;
+  int _margin;
+};
+
+#include "dynamicTextGlyph.I"
+
+#endif  // HAVE_FREETYPE
+
+#endif

+ 50 - 0
panda/src/text/dynamicTextPage.I

@@ -0,0 +1,50 @@
+// Filename: dynamicTextPage.I
+// Created by:  drose (09Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::get_x_size
+//       Access: Published
+//  Description: Returns the x size of the page (texture), in pixels.
+////////////////////////////////////////////////////////////////////
+INLINE int DynamicTextPage::
+get_x_size() const {
+  return _x_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::get_y_size
+//       Access: Published
+//  Description: Returns the y size of the page (texture), in pixels.
+////////////////////////////////////////////////////////////////////
+INLINE int DynamicTextPage::
+get_y_size() const {
+  return _y_size;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::is_empty
+//       Access: Published
+//  Description: Returns true if the page has no glyphs, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool DynamicTextPage::
+is_empty() const {
+  return _glyphs.empty();
+}

+ 140 - 0
panda/src/text/dynamicTextPage.cxx

@@ -0,0 +1,140 @@
+// Filename: dynamicTextPage.cxx
+// Created by:  drose (09Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dynamicTextPage.h"
+#include "dynamicTextFont.h"
+
+#ifdef HAVE_FREETYPE
+
+
+TypeHandle DynamicTextPage::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::Constructor
+//       Access: Publiic
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DynamicTextPage::
+DynamicTextPage(DynamicTextFont *font) : 
+  _font(font)
+{
+  _x_size = _font->get_page_x_size();
+  _y_size = _font->get_page_y_size();
+
+  // Initialize the Texture to an empty, black (transparent) image of
+  // the appropriate size.
+  _pbuffer = new PixelBuffer(_x_size, _y_size, 1, 1, 
+                             PixelBuffer::T_unsigned_byte,
+                             PixelBuffer::F_alpha);
+  mark_dirty(DF_image);
+
+  // We don't necessarily want to use mipmaps, since we don't want to
+  // regenerate those every time the texture changes, but we do want
+  // at least linear filtering.
+  set_magfilter(FT_linear);
+  set_minfilter(FT_linear);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::slot_glyph
+//       Access: Publiic
+//  Description: Finds space within the page for a glyph of the
+//               indicated size.  If space is found, creates a new
+//               glyph object and returns it; otherwise, returns NULL.
+////////////////////////////////////////////////////////////////////
+DynamicTextGlyph *DynamicTextPage::
+slot_glyph(int x_size, int y_size, int margin) {
+  int x, y;
+  if (!find_hole(x, y, x_size, y_size)) {
+    // No room for the glyph.
+    return (DynamicTextGlyph *)NULL;
+  }
+
+  // The glyph can be fit at (x, y).  Slot it.
+  PT(DynamicTextGlyph) glyph = 
+    new DynamicTextGlyph(this, x, y, x_size, y_size, margin);
+  _glyphs.push_back(glyph);
+  return glyph;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::find_hole
+//       Access: Private
+//  Description: Searches for a hole of at least x_size by y_size
+//               pixels somewhere within the page.  If a suitable hole
+//               is found, sets x and y to the top left corner and
+//               returns true; otherwise, returns false.
+////////////////////////////////////////////////////////////////////
+bool DynamicTextPage::
+find_hole(int &x, int &y, int x_size, int y_size) const {
+  y = 0;
+  while (y + y_size <= _y_size) {
+    int next_y = _y_size;
+    // Scan along the row at 'y'.
+    x = 0;
+    while (x + x_size <= _x_size) {
+      int next_x = x;
+
+      // Consider the spot at x, y.
+      DynamicTextGlyph *overlap = find_overlap(x, y, x_size, y_size);
+
+      if (overlap == (DynamicTextGlyph *)NULL) {
+        // Hooray!
+        return true;
+      }
+
+      next_x = overlap->_x + overlap->_x_size;
+      next_y = min(next_y, overlap->_y + overlap->_y_size);
+      nassertr(next_x > x, false);
+      x = next_x;
+    }
+
+    nassertr(next_y > y, false);
+    y = next_y;
+  }
+
+  // Nope, wouldn't fit anywhere.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DynamicTextPage::find_overlap
+//       Access: Private
+//  Description: If the rectangle whose top left corner is x, y and
+//               whose size is x_size, y_size describes an empty hole
+//               that does not overlap any placed glyphs, returns
+//               NULL; otherwise, returns the first placed glyph
+//               that the image does overlap.  It is assumed the
+//               rectangle lies completely within the boundaries of
+//               the page itself.
+////////////////////////////////////////////////////////////////////
+DynamicTextGlyph *DynamicTextPage::
+find_overlap(int x, int y, int x_size, int y_size) const {
+  Glyphs::const_iterator gi;
+  for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
+    DynamicTextGlyph *glyph = (*gi);
+    if (glyph->intersects(x, y, x_size, y_size)) {
+      return glyph;
+    }
+  }
+
+  return (DynamicTextGlyph *)NULL;
+}
+
+
+#endif  // HAVE_FREETYPE

+ 85 - 0
panda/src/text/dynamicTextPage.h

@@ -0,0 +1,85 @@
+// Filename: dynamicTextPage.h
+// Created by:  drose (09Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DYNAMICTEXTPAGE_H
+#define DYNAMICTEXTPAGE_H
+
+#include "pandabase.h"
+
+#ifdef HAVE_FREETYPE
+
+#include "texture.h"
+#include "dynamicTextGlyph.h"
+#include "pointerTo.h"
+#include "pvector.h"
+
+class DynamicTextFont;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DynamicTextPage
+// Description : A single "page" of a DynamicTextFont.  This is a
+//               single texture that holds a number of glyphs for
+//               rendering.  The font starts out with one page, and
+//               will add more as it needs them.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DynamicTextPage : public Texture {
+public:
+  DynamicTextPage(DynamicTextFont *font);
+
+  DynamicTextGlyph *slot_glyph(int x_size, int y_size, int margin);
+
+  INLINE int get_x_size() const;
+  INLINE int get_y_size() const;
+
+PUBLISHED:
+  INLINE bool is_empty() const;
+
+private:
+  bool find_hole(int &x, int &y, int x_size, int y_size) const;
+  DynamicTextGlyph *find_overlap(int x, int y, int x_size, int y_size) const;
+
+  typedef pvector< PT(DynamicTextGlyph) > Glyphs;
+  Glyphs _glyphs;
+
+  int _x_size, _y_size;
+
+  DynamicTextFont *_font;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    Texture::init_type();
+    register_type(_type_handle, "DynamicTextPage",
+                  Texture::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 "dynamicTextPage.I"
+
+#endif  // HAVE_FREETYPE
+
+#endif

+ 4 - 13
panda/src/text/staticTextFont.cxx

@@ -41,6 +41,7 @@ StaticTextFont(Node *font_def) {
   _glyphs.clear();
   _glyphs.clear();
 
 
   find_characters(font_def);
   find_characters(font_def);
+  _is_valid = !_glyphs.empty();
 
 
   if (_font->is_of_type(NamedNode::get_class_type())) {
   if (_font->is_of_type(NamedNode::get_class_type())) {
     NamedNode *named_node = DCAST(NamedNode, _font);
     NamedNode *named_node = DCAST(NamedNode, _font);
@@ -48,16 +49,6 @@ StaticTextFont(Node *font_def) {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: StaticTextFont::Destructor
-//       Access: Published
-//  Description:
-////////////////////////////////////////////////////////////////////
-StaticTextFont::
-~StaticTextFont() {
-}
-
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: StaticTextFont::write
 //     Function: StaticTextFont::write
 //       Access: Published, Virtual
 //       Access: Published, Virtual
@@ -167,13 +158,13 @@ write(ostream &out, int indent_level) const {
 //               code, or NULL if there is no such glyph.
 //               code, or NULL if there is no such glyph.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const TextGlyph *StaticTextFont::
 const TextGlyph *StaticTextFont::
-get_glyph(int character) const {
+get_glyph(int character) {
   Glyphs::const_iterator gi = _glyphs.find(character);
   Glyphs::const_iterator gi = _glyphs.find(character);
   if (gi == _glyphs.end()) {
   if (gi == _glyphs.end()) {
     // No definition for this character.
     // No definition for this character.
     return (TextGlyph *)NULL;
     return (TextGlyph *)NULL;
   } else {
   } else {
-    return &(*gi).second;
+    return (*gi).second;
   }
   }
 }
 }
 
 
@@ -262,7 +253,7 @@ find_characters(Node *root) {
         width = alist[ilist[0]][0];
         width = alist[ilist[0]][0];
       }
       }
 
 
-      _glyphs[character] = TextGlyph(ch, trans, width);
+      _glyphs[character] = new TextGlyph(ch, trans, width);
     }
     }
 
 
   } else if (name == "ds") {
   } else if (name == "ds") {

+ 4 - 12
panda/src/text/staticTextFont.h

@@ -19,17 +19,12 @@
 #ifndef STATICTEXTFONT_H
 #ifndef STATICTEXTFONT_H
 #define STATICTEXTFONT_H
 #define STATICTEXTFONT_H
 
 
-#include <pandabase.h>
+#include "pandabase.h"
 
 
 #include "config_text.h"
 #include "config_text.h"
 #include "textFont.h"
 #include "textFont.h"
 #include "textGlyph.h"
 #include "textGlyph.h"
-
-#include <typedReferenceCount.h>
-#include <namable.h>
-#include <pt_Node.h>
-#include <allTransitionsWrapper.h>
-
+#include "pt_Node.h"
 #include "pmap.h"
 #include "pmap.h"
 
 
 class Node;
 class Node;
@@ -47,19 +42,18 @@ class GeomPoint;
 class EXPCL_PANDA StaticTextFont : public TextFont {
 class EXPCL_PANDA StaticTextFont : public TextFont {
 PUBLISHED:
 PUBLISHED:
   StaticTextFont(Node *font_def);
   StaticTextFont(Node *font_def);
-  virtual ~StaticTextFont();
 
 
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
 
 
 public:
 public:
-  virtual const TextGlyph *get_glyph(int character) const;
+  virtual const TextGlyph *get_glyph(int character);
 
 
 private:
 private:
   bool find_character_gsets(Node *root, Geom *&ch, GeomPoint *&dot,
   bool find_character_gsets(Node *root, Geom *&ch, GeomPoint *&dot,
                             AllTransitionsWrapper &trans);
                             AllTransitionsWrapper &trans);
   void find_characters(Node *root);
   void find_characters(Node *root);
 
 
-  typedef pmap<int, TextGlyph> Glyphs;
+  typedef pmap<int, PT(TextGlyph)> Glyphs;
   Glyphs _glyphs;
   Glyphs _glyphs;
   float _font_height;
   float _font_height;
   PT_Node _font;
   PT_Node _font;
@@ -80,8 +74,6 @@ public:
 
 
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
-
-  friend class TextNode;
 };
 };
 
 
 #include "staticTextFont.I"
 #include "staticTextFont.I"

+ 11 - 0
panda/src/text/textFont.I

@@ -17,6 +17,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextFont::is_valid
+//       Access: Published
+//  Description: Returns true if the font is valid and ready to use,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool TextFont::
+is_valid() const {
+  return _is_valid;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextFont::get_line_height
 //     Function: TextFont::get_line_height
 //       Access: Published
 //       Access: Published

+ 4 - 3
panda/src/text/textFont.cxx

@@ -40,6 +40,7 @@ isblank(char ch) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 TextFont::
 TextFont::
 TextFont() {
 TextFont() {
+  _is_valid = false;
   _line_height = 1.0;
   _line_height = 1.0;
 }
 }
 
 
@@ -59,7 +60,7 @@ TextFont::
 //               or 0.0 if the character is not known.
 //               or 0.0 if the character is not known.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float TextFont::
 float TextFont::
-calc_width(int ch) const {
+calc_width(int ch) {
   if (ch == ' ') {
   if (ch == ' ') {
     // A space is a special case.
     // A space is a special case.
     return 0.25;
     return 0.25;
@@ -82,7 +83,7 @@ calc_width(int ch) const {
 //               character.
 //               character.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float TextFont::
 float TextFont::
-calc_width(const string &line) const {
+calc_width(const string &line) {
   float width = 0.0;
   float width = 0.0;
 
 
   string::const_iterator si;
   string::const_iterator si;
@@ -104,7 +105,7 @@ calc_width(const string &line) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string TextFont::
 string TextFont::
 wordwrap_to(const string &text, float wordwrap_width, 
 wordwrap_to(const string &text, float wordwrap_width, 
-            bool preserve_trailing_whitespace) const {
+            bool preserve_trailing_whitespace) {
   string output_text;
   string output_text;
 
 
   size_t p = 0;
   size_t p = 0;

+ 6 - 4
panda/src/text/textFont.h

@@ -46,19 +46,21 @@ public:
 PUBLISHED:
 PUBLISHED:
   virtual ~TextFont();
   virtual ~TextFont();
 
 
+  INLINE bool is_valid() const;
   INLINE float get_line_height() const;
   INLINE float get_line_height() const;
 
 
-  float calc_width(int ch) const;
-  float calc_width(const string &line) const;
+  float calc_width(int ch);
+  float calc_width(const string &line);
   string wordwrap_to(const string &text, float wordwrap_width,
   string wordwrap_to(const string &text, float wordwrap_width,
-                     bool preserve_trailing_whitespace) const;
+                     bool preserve_trailing_whitespace);
 
 
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
 
 
 public:
 public:
-  virtual const TextGlyph *get_glyph(int character) const=0;
+  virtual const TextGlyph *get_glyph(int character)=0;
 
 
 protected:
 protected:
+  bool _is_valid;
   float _line_height;
   float _line_height;
 
 
 public:
 public:

+ 2 - 0
panda/src/text/textGlyph.I

@@ -24,6 +24,8 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE TextGlyph::
 INLINE TextGlyph::
 TextGlyph() {
 TextGlyph() {
+  _geom = (Geom *)NULL;
+  _advance = 0.0f;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 6 - 4
panda/src/text/textGlyph.h

@@ -21,8 +21,10 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "allTransitionsWrapper.h"
 #include "allTransitionsWrapper.h"
+#include "referenceCount.h"
+#include "geom.h"
+#include "pointerTo.h"
 
 
-class Geom;
 class TextGlyph;
 class TextGlyph;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -31,7 +33,7 @@ class TextGlyph;
 //               font.  This is a piece of renderable geometry of some
 //               font.  This is a piece of renderable geometry of some
 //               kind.
 //               kind.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class TextGlyph {
+class TextGlyph : public ReferenceCount {
 public:
 public:
   INLINE TextGlyph();
   INLINE TextGlyph();
   INLINE TextGlyph(Geom *geom, const AllTransitionsWrapper &trans, float advance);
   INLINE TextGlyph(Geom *geom, const AllTransitionsWrapper &trans, float advance);
@@ -42,8 +44,8 @@ public:
   INLINE const AllTransitionsWrapper &get_trans() const;
   INLINE const AllTransitionsWrapper &get_trans() const;
   INLINE float get_advance() const;
   INLINE float get_advance() const;
 
 
-private:
-  Geom *_geom;
+protected:
+  PT(Geom) _geom;
   AllTransitionsWrapper _trans;
   AllTransitionsWrapper _trans;
   float _advance;
   float _advance;
 };
 };

+ 23 - 1
panda/src/text/textNode.h

@@ -47,7 +47,29 @@ END_PUBLISH
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : TextNode
 //       Class : TextNode
-// Description :
+// Description : The primary interface to this module.  This class
+//               does basic text assembly; given a string of text and
+//               a TextFont object, it creates a piece of geometry
+//               that may be placed in the 3-d or 2-d world to
+//               represent the indicated text.
+//
+//               The TextNode may be used in one of two ways.
+//               Naively, it may be parented to the scene graph
+//               directly; used in this way, you can optionally call
+//               freeze() and thaw() between changing many parameters
+//               in the text at once, to avoid unnecessary expensive
+//               regeneration with each parameter change.  However, it
+//               will work, if slowly, even if you never call freeze()
+//               and thaw().
+//
+//               The second way TextNode may be used is as a text
+//               generator.  To use it in this way, call freeze() once
+//               on the TextNode when you create it, and never call
+//               thaw().  Do not parent the TextNode to the scene
+//               graph; instea, set the properties of the text and
+//               call generate() to return a node which you may parent
+//               wherever you like.  Each time you call generate() a
+//               new node is returned.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA TextNode : public NamedNode {
 class EXPCL_PANDA TextNode : public NamedNode {
 PUBLISHED:
 PUBLISHED:

+ 3 - 0
panda/src/text/text_composite1.cxx

@@ -1,4 +1,7 @@
 #include "config_text.cxx"
 #include "config_text.cxx"
+#include "dynamicTextFont.cxx"
+#include "dynamicTextGlyph.cxx"
+#include "dynamicTextPage.cxx"
 #include "staticTextFont.cxx"
 #include "staticTextFont.cxx"
 #include "textFont.cxx"
 #include "textFont.cxx"
 #include "textGlyph.cxx"
 #include "textGlyph.cxx"