root/OpenSceneGraph/trunk/src/osgPlugins/freetype/FreeTypeFont3D.cpp @ 9881

Revision 9881, 15.9 kB (checked in by robert, 6 years ago)

From David Callu, "Problem:

osgText::Text and osgText::Text3D use the same font file.
The first really load the file and obtain an osgText::Font object,
the second use the cache created during the first load of the
font file, and so obtain an osgText::Font object instead of
osgText::Font3D object. To obtain an osgText::Font3D object,
osgText::Text3D call osgDB::readObjectFile(...) with an option
to specify the plugin we want an osgText::Font3D instead of
osgText::Font.

Generalised Problem:

In osgDB::Registry, loaded file cache is referenced by the name
of this file, so if I load a file with some options, and the cache
already contain object for this filename, I obtain an object
potentially not loaded with my options.

Behaviours:

Cache management is delegate to osgDB::Registry, but cache
coherence (load a file with option then reuse it, deactivate the
cache when load a specific file or don't cached the loaded file)
is user's responsibility.

Text3D solution:

Postfix the font file name by .text3d or something similar and then have the freetype plugin return
osgText::Font3D when it detects this.
This operation is done by osgText::readFont3DFile() which unsure the filename have .text3d as extension.
This is totaly transparent for user, and backward compatible.

BTW, I fix the bug about the Normal of 3D text. Currently, the front and wall face have
the same normal (0,0,1) in the Text3D object coordinate. Now the wall face have its own
normal array computed by the plugin.

BTW 2, I implement
- void Text3D::accept(osg::Drawable::ConstAttributeFunctor?& af) const
- void Text3D::accept(osg::PrimitiveFunctor?& pf) const
so now statistics are well reported.
"

Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include "FreeTypeFont3D.h"
15#include "FreeTypeLibrary.h"
16
17
18#include <limits.h>
19#include <fstream>
20
21
22#include <osg/Geometry>
23#include <osg/Notify>
24#include <osgDB/WriteFile>
25#include <osgUtil/SmoothingVisitor>
26#include <osgUtil/Tessellator>
27
28
29#include <ft2build.h>
30#include FT_FREETYPE_H
31
32#include <freetype/ftoutln.h>
33#include <freetype/ftbbox.h>
34
35
36
37namespace
38{
39
40struct Char3DInfo
41{
42    Char3DInfo(int numSteps=50):
43        _verts( new osg::Vec3Array ),
44        _geometry( new osg::Geometry ),
45        _idx(0),
46        _numSteps(numSteps),
47        _maxY(-FLT_MAX),
48        _maxX(-FLT_MAX),
49        _minX(FLT_MAX),
50        _minY(FLT_MAX)
51    {
52    }
53    ~Char3DInfo()
54    {
55    }
56
57    osg::Geometry* get()
58    {
59        int len = _verts->size()-_idx;
60        if (len)
61        {
62            _geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, _idx, len ) );
63            _idx = _verts->size();
64        }
65
66        _geometry->setVertexArray(_verts.get());
67        return _geometry.get();
68    }
69
70    void moveTo(osg::Vec2 pos)
71    {
72        if (_verts->size())
73        {
74            int len = _verts->size()-_idx;
75            _geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, _idx, len ) );
76        }
77        _idx = _verts->size();
78        _verts->push_back( osg::Vec3(pos.x(),pos.y(),0) );
79
80        setMinMax(pos);
81    }
82    void lineTo(osg::Vec2 pos)
83    {
84        _verts->push_back( osg::Vec3(pos.x(),pos.y(),0) );
85        setMinMax(pos);
86    }
87    void conicTo(osg::Vec2 control, osg::Vec2 pos)
88    {
89        osg::Vec3 p0 = _verts->back();
90        osg::Vec3 p1 = osg::Vec3(control.x(),control.y(),0);
91        osg::Vec3 p2 = osg::Vec3(pos.x(),pos.y(),0);
92
93        double dt = 1.0/_numSteps;
94        double u=0;
95        for (int i=0; i<=_numSteps; ++i)
96        {
97            double w = 1;
98            double bs = 1.0/( (1-u)*(1-u)+2*(1-u)*u*w +u*u );
99            osg::Vec3 p = (p0*((1-u)*(1-u)) + p1*(2*(1-u)*u*w) + p2*(u*u))*bs;
100            _verts->push_back( p );
101
102            u += dt;
103        }
104
105        setMinMax(pos);
106    }
107
108    void cubicTo(osg::Vec2 control1, osg::Vec2 control2, osg::Vec2 pos)
109    {
110        osg::Vec3 p0 = _verts->back();
111        osg::Vec3 p1 = osg::Vec3(control1.x(),control1.y(),0);
112        osg::Vec3 p2 = osg::Vec3(control2.x(),control2.y(),0);
113        osg::Vec3 p3 = osg::Vec3(pos.x(),pos.y(),0);
114
115        double cx = 3*(p1.x() - p0.x());
116        double bx = 3*(p2.x() - p1.x()) - cx;
117        double ax = p3.x() - p0.x() - cx - bx;
118        double cy = 3*(p1.y() - p0.y());
119        double by = 3*(p2.y() - p1.y()) - cy;
120        double ay = p3.y() - p0.y() - cy - by;
121
122        double dt = 1.0/_numSteps;
123        double u=0;
124        for (int i=0; i<=_numSteps; ++i)
125        {
126            osg::Vec3 p = osg::Vec3( ax*u*u*u + bx*u*u  + cx*u + p0.x(),ay*u*u*u + by*u*u  + cy*u + p0.y(),0 );
127            _verts->push_back( p );
128
129            u += dt;
130        }
131
132        setMinMax(pos);
133    }
134
135    void setMinMax(osg::Vec2 pos)
136    {
137        _maxY = std::max(_maxY, (double) pos.y());
138        _minY = std::min(_minY, (double) pos.y());
139        _maxX = std::max(_maxX, (double) pos.x());
140        _minX = std::min(_minX, (double) pos.x());
141    }
142
143    osg::ref_ptr<osg::Vec3Array>    _verts;
144    osg::ref_ptr<osg::Geometry>     _geometry;
145    int                             _idx;
146    int                             _numSteps;
147    double                          _maxY;
148    double                          _maxX;
149    double                          _minX;
150    double                          _minY;
151};
152
153
154#define FT_NUM(x) (x/64.0)
155int moveTo( const FT_Vector* to, void* user )
156{
157    Char3DInfo* char3d = (Char3DInfo*)user;
158    char3d->moveTo( osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) );
159    return 0;
160}
161int lineTo( const FT_Vector* to, void* user )
162{
163    Char3DInfo* char3d = (Char3DInfo*)user;
164    char3d->lineTo( osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) );
165    return 0;
166}
167int conicTo( const FT_Vector* control,const FT_Vector* to, void* user )
168{
169    Char3DInfo* char3d = (Char3DInfo*)user;
170    char3d->conicTo( osg::Vec2(FT_NUM(control->x),FT_NUM(control->y)), osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) );
171    return 0;
172}
173int cubicTo( const FT_Vector* control1,const FT_Vector* control2,const FT_Vector* to, void* user )
174{
175    Char3DInfo* char3d = (Char3DInfo*)user;
176    char3d->cubicTo(
177        osg::Vec2(FT_NUM(control1->x),FT_NUM(control1->y)),
178        osg::Vec2(FT_NUM(control2->x),FT_NUM(control2->y)),
179        osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) );
180    return 0;
181}
182#undef FT_NUM
183
184}
185
186
187FreeTypeFont3D::FreeTypeFont3D(const std::string& filename, FT_Face face, unsigned int flags):
188    _filename(filename),
189    _buffer(0),
190    _face(face),
191    _flags(flags),
192    _scale(1.0),
193    _shiftY(0.0),
194    _shiftX(0.0),
195    _charScale(1.0)
196{
197    init();
198}
199
200FreeTypeFont3D::FreeTypeFont3D(FT_Byte* buffer, FT_Face face, unsigned int flags):
201    _filename(""),
202    _buffer(buffer),
203    _face(face),
204    _flags(flags),
205    _scale(1.0),
206    _shiftY(0.0),
207    _shiftX(0.0),
208    _charScale(1.0)
209{
210    init();
211}
212
213void FreeTypeFont3D::init()
214{
215
216    FT_Error _error = FT_Set_Pixel_Sizes(_face, 32, 32);
217    if (_error)
218    {
219        osg::notify(osg::NOTICE) << "FreeTypeFont3D: set pixel sizes failed ..." << std::endl;
220        return;
221    }
222
223    FT_Set_Char_Size( _face, 64*64, 64*64, 600, 600);
224
225    int glyphIndex = FT_Get_Char_Index( _face, 'M' );
226    _error = FT_Load_Glyph( _face, glyphIndex, FT_LOAD_DEFAULT );
227    if (_error)
228    {
229        osg::notify(osg::NOTICE) << "FreeTypeFont3D: initial glyph load failed ..." << std::endl;
230        return;
231    }
232
233    if (_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
234    {
235        osg::notify(osg::NOTICE) << "FreeTypeFont3D: not a vector font" << std::endl;
236        return;
237    }
238
239    {
240        Char3DInfo char3d;
241
242        FT_Outline outline = _face->glyph->outline;
243        FT_Outline_Funcs funcs;
244        funcs.conic_to = (FT_Outline_ConicToFunc)&conicTo;
245        funcs.line_to = (FT_Outline_LineToFunc)&lineTo;
246        funcs.cubic_to = (FT_Outline_CubicToFunc)&cubicTo;
247        funcs.move_to = (FT_Outline_MoveToFunc)&moveTo;
248        funcs.shift = 0;
249        funcs.delta = 0;
250        _error = FT_Outline_Decompose(&outline,&funcs,&char3d);
251        if (_error)
252        {
253            osg::notify(osg::NOTICE) << "FreeTypeFont3D: - outline decompose failed ..." << std::endl;
254            return;
255        }
256
257        FT_BBox bb;
258        FT_Outline_Get_BBox(&outline,&bb);
259
260        long xmin = ft_floor( bb.xMin );
261        long xmax = ft_ceiling( bb.xMax );
262        long ymin = ft_floor( bb.yMin );
263        long ymax = ft_ceiling( bb.yMax );
264
265        double width = (xmax - xmin)/64.0;
266        double height = (ymax - ymin)/64.0;
267
268        _scale = 1.0/height;
269
270        double charHeight = char3d._maxY-char3d._minY;
271        double charWidth = char3d._maxX-char3d._minX;
272
273        double dh = fabs(bb.yMin/64.0)/height;
274        double dw = fabs(bb.xMin/64.0)/width;
275
276        _shiftY = char3d._minY + dh*charHeight;
277        _shiftX = char3d._minX + dw*charWidth;
278
279        _charScale = 1/charHeight;
280    }
281}
282
283FreeTypeFont3D::~FreeTypeFont3D()
284{
285    if(_face)
286    {
287        FreeTypeLibrary* freeTypeLibrary = FreeTypeLibrary::instance();
288        if (freeTypeLibrary)
289        {
290            // remove myself from the local registry to ensure that
291            // not dangling pointers remain
292            freeTypeLibrary->removeFont3DImplmentation(this);
293
294            // free the freetype font face itself
295            FT_Done_Face(_face);
296            _face = 0;
297
298            // release memory held for FT_Face to work
299            if (_buffer)
300            {
301                delete [] _buffer;
302                _buffer = 0;
303            }
304        }
305    }
306}
307
308
309osgText::Font3D::Glyph3D * FreeTypeFont3D::getGlyph(unsigned int charcode)
310{
311
312
313    //
314    // GT: fix for symbol fonts (i.e. the Webdings font) as the wrong character are being
315    // returned, for symbol fonts in windows (FT_ENCONDING_MS_SYMBOL in freetype) the correct
316    // values are from 0xF000 to 0xF0FF not from 0x000 to 0x00FF (0 to 255) as you would expect.
317    // Microsoft uses a private field for its symbol fonts
318    //
319    unsigned int charindex = charcode;
320    if (_face->charmap != NULL)
321    {
322        if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL)
323        {
324            charindex |= 0xF000;
325        }
326    }
327
328    FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_DEFAULT|_flags );
329    if (error)
330    {
331        osg::notify(osg::WARN) << "FT_Load_Char(...) error 0x"<<std::hex<<error<<std::dec<<std::endl;
332        return 0;
333    }
334    if (_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
335    {
336        osg::notify(osg::WARN) << "FreeTypeFont3D::getGlyph : not a vector font" << std::endl;
337        return 0;
338    }
339
340    // ** init FreeType to describe the glyph
341    Char3DInfo char3d;
342
343    FT_Outline outline = _face->glyph->outline;
344    FT_Outline_Funcs funcs;
345    funcs.conic_to = (FT_Outline_ConicToFunc)&conicTo;
346    funcs.line_to = (FT_Outline_LineToFunc)&lineTo;
347    funcs.cubic_to = (FT_Outline_CubicToFunc)&cubicTo;
348    funcs.move_to = (FT_Outline_MoveToFunc)&moveTo;
349    funcs.shift = 0;
350    funcs.delta = 0;
351
352    // ** record description
353    FT_Error _error = FT_Outline_Decompose(&outline, &funcs, &char3d);
354    if (_error)
355    {
356        osg::notify(osg::WARN) << "FreeTypeFont3D::getGlyph : - outline decompose failed ..." << std::endl;
357        return 0;
358    }
359
360    // ** create geometry for each part of the glyph
361    osg::ref_ptr<osg::Geometry> frontGeo(new osg::Geometry);
362    frontGeo->setVertexArray(char3d.get()->getVertexArray());
363    frontGeo->setPrimitiveSetList(char3d.get()->getPrimitiveSetList());
364
365    osg::ref_ptr<osg::Geometry> wallGeo(new osg::Geometry);
366    wallGeo->setVertexArray(frontGeo->getVertexArray());
367
368    osg::ref_ptr<osg::Geometry> backGeo(new osg::Geometry);
369    backGeo->setVertexArray(frontGeo->getVertexArray());
370
371
372
373    // ** for convenience.
374    osg::Vec3Array * vertices = char3d._verts.get();
375
376
377
378    // ** duplicate the vertex for the back face
379    // ** with a depth equal to the font depth
380    std::size_t len = vertices->size();
381    std::size_t dlen = len * 2;
382
383    vertices->reserve(dlen);
384
385    osg::Vec3Array::iterator begin = vertices->begin();
386    osg::Vec3Array::iterator it = vertices->begin();
387
388    for (std::size_t i = 0; i != len; ++i, ++it)
389        vertices->push_back(*it);
390//    std::copy(begin, begin + len, begin + len + 1); TOCHECK
391
392
393    // ** and decal new vertices
394    unsigned int depth = _facade->getFontDepth();
395    for (std::size_t i = len; i != dlen; ++i)
396    {
397        (*vertices)[i].z() -= depth;
398    }
399
400    osg::Vec3Array::iterator end;
401
402    // ** create wall and back face from the front polygon
403    // **  then accumulate them in the appropriate geometry wallGeo and backGeo
404    for (std::size_t i=0; i < frontGeo->getNumPrimitiveSets(); ++i)
405    {
406        // ** get the front polygon
407        osg::ref_ptr<osg::DrawArrays> daFront(dynamic_cast<osg::DrawArrays*>(frontGeo->getPrimitiveSet(i)));
408        unsigned int idx = daFront->getFirst();
409        unsigned int cnt = daFront->getCount();
410
411        // ** reverse vertices to draw the front face in the CCW
412        std::reverse(begin + idx, begin + idx + cnt);
413
414        // ** create the back polygon
415        osg::ref_ptr<osg::DrawArrays> daBack(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, idx + len, cnt));
416        backGeo->addPrimitiveSet(daBack.get());
417
418
419        // ** create the wall triangle strip
420        osg::ref_ptr<osg::DrawElementsUInt> deWall(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP));
421        wallGeo->addPrimitiveSet(deWall.get());
422
423        // **  link triangle strip
424        deWall->push_back(idx + len);
425        for (unsigned int j = 1; j < cnt; ++j)
426        {
427            deWall->push_back(idx + cnt - j);
428            deWall->push_back(idx + len + j);
429        }
430        deWall->push_back(idx);
431        deWall->push_back(idx + len);
432        deWall->push_back(idx + cnt - 1);
433    }
434
435    // ** tesselate front and back face
436    {
437        osgUtil::Tessellator ts;
438        ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
439        ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
440        ts.retessellatePolygons(*frontGeo);
441    }
442
443    {
444        osgUtil::Tessellator ts;
445        ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
446        ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
447        ts.retessellatePolygons(*backGeo);
448    }
449
450    // ** generate normal
451    {
452        osgUtil::SmoothingVisitor sm;
453        osg::ref_ptr<osg::Geode> geode(new osg::Geode);
454        geode->addDrawable(wallGeo.get());
455        geode->accept(sm);
456    }
457
458    // ** save vertices and PrimitiveSetList of each face in the Glyph3D PrimitiveSet face list
459    osgText::Font3D::Glyph3D * glyph3D = new osgText::Font3D::Glyph3D(charcode);
460
461    glyph3D->setVertexArray(dynamic_cast<osg::Vec3Array*>(frontGeo->getVertexArray()));
462    glyph3D->setNormalArray(dynamic_cast<osg::Vec3Array*>(wallGeo->getNormalArray()));
463
464    glyph3D->getFrontPrimitiveSetList() = frontGeo->getPrimitiveSetList();
465    glyph3D->getWallPrimitiveSetList() = wallGeo->getPrimitiveSetList();
466    glyph3D->getBackPrimitiveSetList() = backGeo->getPrimitiveSetList();
467
468
469    FT_Glyph_Metrics* metrics = &(_face->glyph->metrics);
470
471    glyph3D->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX/64.0f,(float)(metrics->horiBearingY-metrics->height)/64.0f)); // bottom left.
472    glyph3D->setHorizontalAdvance((float)metrics->horiAdvance/64.0f);
473    glyph3D->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX/64.0f,(float)(metrics->vertBearingY-metrics->height)/64.0f)); // top middle.
474    glyph3D->setVerticalAdvance((float)metrics->vertAdvance/64.0f);
475
476    glyph3D->setWidth((float)metrics->width / 64.0f);
477    glyph3D->setHeight((float)metrics->height / 64.0f);
478
479
480    FT_BBox ftbb;
481    FT_Outline_Get_BBox(&outline, &ftbb);
482
483    long xmin = ft_floor( ftbb.xMin );
484    long xmax = ft_ceiling( ftbb.xMax );
485    long ymin = ft_floor( ftbb.yMin );
486    long ymax = ft_ceiling( ftbb.yMax );
487
488    osg::BoundingBox bb(xmin / 64.0f, ymin / 64.0f, 0.0f, xmax / 64.0f, ymax / 64.0f, 0.0f);
489
490    glyph3D->setBoundingBox(bb);
491
492    return glyph3D;
493}
494
495osg::Vec2 FreeTypeFont3D::getKerning(unsigned int leftcharcode,unsigned int rightcharcode, osgText::KerningType kerningType)
496{
497    if (!FT_HAS_KERNING(_face) || (kerningType == osgText::KERNING_NONE)) return osg::Vec2(0.0f,0.0f);
498
499    FT_Kerning_Mode mode = (kerningType==osgText::KERNING_DEFAULT) ? ft_kerning_default : ft_kerning_unfitted;
500
501    // convert character code to glyph index
502    FT_UInt left = FT_Get_Char_Index( _face, leftcharcode );
503    FT_UInt right = FT_Get_Char_Index( _face, rightcharcode );
504
505    // get the kerning distances.
506    FT_Vector  kerning;
507
508    FT_Error error = FT_Get_Kerning( _face,                     // handle to face object
509                                     left,                      // left glyph index
510                                     right,                     // right glyph index
511                                     mode,                      // kerning mode
512                                     &kerning );                // target vector
513
514    if (error)
515    {
516        osg::notify(osg::WARN) << "FT_Get_Kerning(...) returned error code " <<std::hex<<error<<std::dec<< std::endl;
517        return osg::Vec2(0.0f,0.0f);
518    }
519
520    return osg::Vec2((float)kerning.x/64.0f,(float)kerning.y/64.0f);
521}
522
523bool FreeTypeFont3D::hasVertical() const
524{
525    return FT_HAS_VERTICAL(_face)!=0;
526}
527
528float FreeTypeFont3D::getScale() const
529{
530    return _scale;
531}
Note: See TracBrowser for help on using the browser.