root/OpenSceneGraph/trunk/examples/osgtext3D/osgtext3D.cpp @ 11685

Revision 11685, 17.2 kB (checked in by robert, 4 years ago)

Added bevel geometry

Line 
1/* OpenSceneGraph example, osgtext.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include <osg/ArgumentParser>
20#include <osg/Geode>
21#include <osg/Geometry>
22#include <osg/PositionAttitudeTransform>
23#include <osgUtil/SmoothingVisitor>
24#include <osgText/Font3D>
25#include <osgDB/WriteFile>
26#include <osgGA/StateSetManipulator>
27#include <osgUtil/Tessellator>
28#include <osgViewer/Viewer>
29#include <osgViewer/ViewerEventHandlers>
30#include <osg/io_utils>
31
32extern int main_orig(int, char**);
33extern int main_test(int, char**);
34
35
36class Boundary
37{
38public:
39
40    typedef std::pair<unsigned int, unsigned int> Segment;
41    typedef std::vector<Segment>  Segments;
42    osg::ref_ptr<osg::Vec3Array> _vertices;
43    unsigned int _start;
44    unsigned int _count;
45    Segments _segments;
46
47    Boundary(osg::Vec3Array* vertices, unsigned int start, unsigned int count)
48    {
49        _vertices = vertices;
50        _start = start;
51        _count = count;
52
53        if ((*_vertices)[start]==(*_vertices)[start+count-1])
54        {
55            // OSG_NOTICE<<"Boundary is a line loop"<<std::endl;
56        }
57        else
58        {
59            OSG_NOTICE<<"Boundary is not a line loop"<<std::endl;
60        }
61
62        _segments.reserve(count-1);
63        for(unsigned int i=start; i<start+count-2; ++i)
64        {
65            _segments.push_back(Segment(i,i+1));
66        }
67        _segments.push_back(Segment(start+count-2,start));
68
69    }
70
71    osg::Vec3 computeRayIntersectionPoint(const osg::Vec3& a, const osg::Vec3& an, const osg::Vec3& c, const osg::Vec3& cn)
72    {
73        float denominator = ( cn.x() * an.y() - cn.y() * an.x());
74        if (denominator==0.0f)
75        {
76            //OSG_NOTICE<<"computeRayIntersectionPoint()<<denominator==0.0"<<std::endl;
77            // line segments must be parallel.
78            return (a+c)*0.5f;
79        }
80
81        float t = ((a.x()-c.x())*an.y() - (a.y()-c.y())*an.x()) / denominator;
82        return c + cn*t;
83    }
84
85    osg::Vec3 computeIntersectionPoint(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d)
86    {
87        return computeRayIntersectionPoint(a, b-a, c, d-c);
88    }
89
90    osg::Vec3 computeBisectorNormal(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d)
91    {
92        osg::Vec2 ab(a.x()-b.x(), a.y()-b.y());
93        osg::Vec2 dc(d.x()-c.x(), d.y()-c.y());
94        /*float length_ab =*/ ab.normalize();
95        /*float length_dc =*/ dc.normalize();
96
97        float e = dc.y() - ab.y();
98        float f = ab.x() - dc.x();
99        float denominator = sqrtf(e*e + f*f);
100        float nx = e / denominator;
101        float ny = f / denominator;
102        if (( ab.x()*ny - ab.y()*nx) > 0.0f)
103        {
104            // OSG_NOTICE<<"   computeBisectorNormal(a=["<<a<<"], b=["<<b<<"], c=["<<c<<"], d=["<<d<<"]), nx="<<nx<<", ny="<<ny<<", denominator="<<denominator<<" no need to swap"<<std::endl;
105            return osg::Vec3(nx,ny,0.0f);
106        }
107        else
108        {
109            OSG_NOTICE<<"   computeBisectorNormal(a=["<<a<<"], b=["<<b<<"], c=["<<c<<"], d=["<<d<<"]), nx="<<nx<<", ny="<<ny<<", denominator="<<denominator<<" need to swap!!!"<<std::endl;
110            return osg::Vec3(-nx,-ny,0.0f);
111        }
112    }
113
114    float computeBisectorIntersectorThickness(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d, const osg::Vec3& e, const osg::Vec3& f)
115    {
116        osg::Vec3 intersection_abcd = computeIntersectionPoint(a,b,c,d);
117        osg::Vec3 bisector_abcd = computeBisectorNormal(a,b,c,d);
118        osg::Vec3 intersection_cdef = computeIntersectionPoint(c,d,e,f);
119        osg::Vec3 bisector_cdef = computeBisectorNormal(c,d,e,f);
120        if (bisector_abcd==bisector_cdef)
121        {
122            //OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
123            //OSG_NOTICE<<"   bisectors parallel, thickness = "<<FLT_MAX<<std::endl;
124            return FLT_MAX;
125        }
126
127        osg::Vec3 bisector_intersection = computeRayIntersectionPoint(intersection_abcd,bisector_abcd, intersection_cdef, bisector_cdef);
128        osg::Vec3 normal(d.y()-c.y(), c.x()-d.x(), 0.0);
129        float cd_length = normal.normalize();
130        if (cd_length==0)
131        {
132            //OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
133            //OSG_NOTICE<<"   segment length==0, thickness = "<<FLT_MAX<<std::endl;
134            return FLT_MAX;
135        }
136
137        float thickness = (bisector_intersection - c) * normal;
138    #if 0
139        OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
140        OSG_NOTICE<<"   bisector_abcd = "<<bisector_abcd<<", bisector_cdef="<<bisector_cdef<<std::endl;
141        OSG_NOTICE<<"   bisector_intersection = "<<bisector_intersection<<", thickness = "<<thickness<<std::endl;
142    #endif
143        return thickness;
144    }
145
146
147    float computeThickness(unsigned int i)
148    {
149        Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
150        Segment& seg_target = _segments[ (i) % _segments.size() ];
151        Segment& seg_after =  _segments[ (i+1) % _segments.size() ];
152        return computeBisectorIntersectorThickness(
153            (*_vertices)[seg_before.first], (*_vertices)[seg_before.second],
154            (*_vertices)[seg_target.first], (*_vertices)[seg_target.second],
155            (*_vertices)[seg_after.first], (*_vertices)[seg_after.second]);
156    }
157
158    void computeAllThickness()
159    {
160        for(unsigned int i=0; i<_segments.size(); ++i)
161        {
162            computeThickness(i);
163        }
164    }
165
166
167    bool findMinThickness(unsigned int& minThickness_i, float& minThickness)
168    {
169        minThickness_i = _segments.size();
170        for(unsigned int i=0; i<_segments.size(); ++i)
171        {
172            float thickness = computeThickness(i);
173            if (thickness>0.0 && thickness <  minThickness)
174            {
175                minThickness = thickness;
176                minThickness_i = i;
177            }
178        }
179
180        return minThickness_i != _segments.size();
181    }
182
183    void removeAllSegmentsBelowThickness(float targetThickness)
184    {
185        // OSG_NOTICE<<"removeAllSegmentsBelowThickness("<<targetThickness<<")"<<std::endl;
186        for(;;)
187        {
188            unsigned int minThickness_i = _segments.size();
189            float minThickness = targetThickness;
190            if (!findMinThickness(minThickness_i,minThickness)) break;
191
192            // OSG_NOTICE<<"  removing segment _segments["<<minThickness_i<<"] ("<<_segments[minThickness_i].first<<", "<<_segments[minThickness_i].second<<" with thickness="<<minThickness<<" "<<std::endl;
193            _segments.erase(_segments.begin()+minThickness_i);
194        }
195    }
196
197    osg::Vec3 computeBisectorPoint(unsigned int i, float targetThickness)
198    {
199        Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
200        Segment& seg_target = _segments[ (i) % _segments.size() ];
201        osg::Vec3& a = (*_vertices)[seg_before.first];
202        osg::Vec3& b = (*_vertices)[seg_before.second];
203        osg::Vec3& c = (*_vertices)[seg_target.first];
204        osg::Vec3& d = (*_vertices)[seg_target.second];
205        osg::Vec3 intersection_abcd = computeIntersectionPoint(a,b,c,d);
206        osg::Vec3 bisector_abcd = computeBisectorNormal(a,b,c,d);
207        osg::Vec3 ab_sidevector(b.y()-a.y(), a.x()-b.x(), 0.0);
208        ab_sidevector.normalize();
209        float scale_factor = 1.0/ (bisector_abcd*ab_sidevector);
210        osg::Vec3 new_vertex = intersection_abcd + bisector_abcd*(scale_factor*targetThickness);
211
212        // OSG_NOTICE<<"bisector_abcd = "<<bisector_abcd<<", ab_sidevector="<<ab_sidevector<<", b-a="<<b-a<<", scale_factor="<<scale_factor<<std::endl;
213
214        new_vertex.z() += 5.0f;
215        return new_vertex;
216    }
217
218    void addBoundaryToGeometry(osg::Geometry* geometry, float targetThickness)
219    {
220        if (_segments.empty()) return;
221
222        if (geometry->getVertexArray()==0) geometry->setVertexArray(new osg::Vec3Array);
223        osg::Vec3Array* new_vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
224
225        // allocate the primitive set to store the face geometry
226        osg::DrawElementsUShort* face = new osg::DrawElementsUShort(GL_POLYGON);
227        face->setName("face");
228
229        // reserve enough space in the vertex array to accomodate the vertices associated with the segments
230        new_vertices->reserve(new_vertices->size() + _segments.size()+1 + _count);
231
232        // create vertices
233        unsigned int previous_second = _segments[0].second;
234        osg::Vec3 newPoint = computeBisectorPoint(0, targetThickness);
235        unsigned int first = new_vertices->size();
236        new_vertices->push_back(newPoint);
237
238        if (_segments[0].first != _start)
239        {
240            //OSG_NOTICE<<"We have pruned from the start"<<std::endl;
241            for(unsigned int j=_start; j<=_segments[0].first;++j)
242            {
243                face->push_back(first);
244            }
245        }
246        else
247        {
248            face->push_back(first);
249        }
250
251
252        for(unsigned int i=1; i<_segments.size(); ++i)
253        {
254            newPoint = computeBisectorPoint(i, targetThickness);
255            unsigned int vi = new_vertices->size();
256            new_vertices->push_back(newPoint);
257
258            if (previous_second != _segments[i].first)
259            {
260                //OSG_NOTICE<<"Gap in boundary"<<previous_second<<" to "<<_segments[i].first<<std::endl;
261                for(unsigned int j=previous_second; j<=_segments[i].first;++j)
262                {
263                    face->push_back(vi);
264                }
265            }
266            else
267            {
268                face->push_back(vi);
269            }
270
271            previous_second = _segments[i].second;
272        }
273
274        // fill the end of the polygon with repititions of the first index in the polygon to ensure
275        // that the orignal and new boundary polygons have the same number and pairing of indices.
276        // This ensures that the bevel can be created coherently.
277        while(face->size() < _count)
278        {
279            face->push_back(first);
280        }
281
282        // add face primitive set for polygon
283        geometry->addPrimitiveSet(face);
284
285        osg::DrawElementsUShort* bevel = new osg::DrawElementsUShort(GL_QUAD_STRIP);
286        bevel->setName("bevel");
287        bevel->reserve(_count*2);
288        for(unsigned int i=0; i<_count; ++i)
289        {
290            unsigned int vi = new_vertices->size();
291            new_vertices->push_back((*_vertices)[_start+i]);
292            bevel->push_back(vi);
293            bevel->push_back((*face)[i]);
294        }
295        geometry->addPrimitiveSet(bevel);
296    }
297
298};
299
300osg::Geometry* getGeometryComponent(osg::Geometry* geometry, bool bevel)
301{
302    osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
303    if (!vertices) return 0;
304
305    osg::Geometry* new_geometry = new osg::Geometry;
306    osg::Vec3Array* new_vertices = new osg::Vec3Array(*vertices);
307    new_geometry->setVertexArray(new_vertices);
308
309    for(unsigned int i=0; i<geometry->getNumPrimitiveSets(); ++i)
310    {
311        osg::PrimitiveSet* primitiveSet = geometry->getPrimitiveSet(i);
312        if (primitiveSet->getName()=="bevel")
313        {
314            if (bevel) new_geometry->addPrimitiveSet(primitiveSet);
315        }
316        else
317        {
318            if (!bevel) new_geometry->addPrimitiveSet(primitiveSet);
319        }
320    }
321
322    osg::Vec4Array* new_colours = new osg::Vec4Array;
323    new_colours->push_back(bevel ? osg::Vec4(1.0,1.0,0.0,1.0) : osg::Vec4(1.0,0.0,0.0,1.0));
324    new_geometry->setColorArray(new_colours);
325    new_geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
326
327    if (!bevel)
328    {
329        osg::Vec3Array* normals = new osg::Vec3Array;
330        normals->push_back(osg::Vec3(0.0,0.0,1.0));
331        new_geometry->setNormalArray(normals);
332        new_geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
333    }
334
335    return new_geometry;
336}
337
338
339osg::Geometry* computeThickness(osg::Geometry* orig_geometry, float thickness)
340{
341    // OSG_NOTICE<<"computeThickness("<<orig_geometry<<")"<<std::endl;
342    osg::Vec3Array* orig_vertices = dynamic_cast<osg::Vec3Array*>(orig_geometry->getVertexArray());
343    osg::Geometry::PrimitiveSetList& orig_primitives = orig_geometry->getPrimitiveSetList();
344
345    osg::Geometry* new_geometry = new osg::Geometry;
346
347    for(osg::Geometry::PrimitiveSetList::iterator itr = orig_primitives.begin();
348        itr != orig_primitives.end();
349        ++itr)
350    {
351        osg::DrawArrays* drawArray = dynamic_cast<osg::DrawArrays*>(itr->get());
352        if (drawArray && drawArray->getMode()==GL_POLYGON)
353        {
354            Boundary boundary(orig_vertices, drawArray->getFirst(), drawArray->getCount());
355            boundary.removeAllSegmentsBelowThickness(thickness);
356            boundary.addBoundaryToGeometry(new_geometry, thickness);
357        }
358    }
359    return new_geometry;
360}
361
362int main(int argc, char** argv)
363{
364    osg::ArgumentParser arguments(&argc, argv);
365
366    if (arguments.read("--test"))
367    {
368        return main_test(argc,argv);
369    }
370    else if (arguments.read("--original") || arguments.read("--orig"))
371    {
372        return main_orig(argc,argv);
373    }
374
375    std::string fontFile("arial.ttf");
376    while(arguments.read("-f",fontFile)) {}
377
378    std::string word("This is a simple test");
379
380    while(arguments.read("--ascii"))
381    {
382        word.clear();
383        for(unsigned int c=' '; c<=127;++c)
384        {
385            word.push_back(c);
386        }
387    }
388
389    while(arguments.read("-w",word)) {}
390
391    osg::ref_ptr<osgText::Font3D> font = osgText::readFont3DFile(fontFile);
392    if (!font) return 1;
393    OSG_NOTICE<<"Read font "<<fontFile<<" font="<<font.get()<<std::endl;
394
395    bool useTessellator = false;
396    while(arguments.read("-t") || arguments.read("--tessellate")) { useTessellator = true; }
397
398    float thickness = 5.0;
399    while(arguments.read("--thickness",thickness)) {}
400
401    osg::ref_ptr<osg::Group> group = new osg::Group;
402    osg::Vec3 position;
403
404    for(unsigned int i=0; i<word.size(); ++i)
405    {
406        osg::ref_ptr<osgText::Font3D::Glyph3D> glyph = font->getGlyph(word[i]);
407        if (!glyph) return 1;
408
409        osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
410        transform->setPosition(position);
411        transform->setAttitude(osg::Quat(osg::inDegrees(90.0),osg::Vec3d(1.0,0.0,0.0)));
412
413        position.x() += glyph->getHorizontalWidth();
414
415        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
416
417        osg::Vec3Array* vertices = glyph->getRawVertexArray();
418        osg::Geometry::PrimitiveSetList& primitives = glyph->getRawFacePrimitiveSetList();
419
420        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
421        geometry->setVertexArray(vertices);
422        geometry->setPrimitiveSetList(primitives);
423        osg::Vec4Array* colours = new osg::Vec4Array;
424        colours->push_back(osg::Vec4(1.0,1.0,1.0,1.0));
425        geometry->setColorArray(colours);
426        geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
427
428        osg::ref_ptr<osg::Geometry> face_and_bevel = computeThickness(geometry, thickness);
429
430        osg::Geometry* bevel = getGeometryComponent(face_and_bevel, true);
431        if (bevel)
432        {
433            geode->addDrawable(bevel);
434            osgUtil::SmoothingVisitor smoother;
435            smoother.smooth(*bevel);
436        }
437
438        osg::Geometry* face = getGeometryComponent(face_and_bevel, false);
439        if (face) geode->addDrawable(face);
440
441   
442        if (useTessellator)
443        {
444            if (geometry)
445            {
446                osgUtil::Tessellator ts;
447                ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
448                ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
449                ts.retessellatePolygons(*geometry);
450            }
451
452            if (face)
453            {
454                osgUtil::Tessellator ts;
455                ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
456                ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
457                ts.retessellatePolygons(*face);
458            }
459
460        }
461
462        geode->addDrawable(geometry.get());
463
464        transform->addChild(geode.get());
465
466        group->addChild(transform.get());
467    }
468
469
470
471    std::string filename;
472    if (arguments.read("-o", filename)) osgDB::writeNodeFile(*group, filename);
473
474    osgViewer::Viewer viewer(arguments);
475    viewer.setSceneData(group.get());
476    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
477    return viewer.run();
478}
Note: See TracBrowser for help on using the browser.