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

Revision 11684, 14.2 kB (checked in by robert, 4 years ago)

Clean up boudnary code

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 <osgText/Font3D>
24#include <osgDB/WriteFile>
25#include <osgGA/StateSetManipulator>
26#include <osgUtil/Tessellator>
27#include <osgViewer/Viewer>
28#include <osgViewer/ViewerEventHandlers>
29#include <osg/io_utils>
30
31extern int main_orig(int, char**);
32extern int main_test(int, char**);
33
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    Segments _segments;
44
45    Boundary(osg::Vec3Array* vertices, unsigned int start, unsigned int count)
46    {
47        _vertices = vertices;
48
49        if ((*_vertices)[start]==(*_vertices)[start+count-1])
50        {
51            // OSG_NOTICE<<"Boundary is a line loop"<<std::endl;
52        }
53        else
54        {
55            OSG_NOTICE<<"Boundary is not a line loop"<<std::endl;
56        }
57
58        _segments.reserve(count-1);
59        for(unsigned int i=start; i<start+count-1; ++i)
60        {
61            _segments.push_back(Segment(i,i+1));
62        }
63
64    }
65
66    osg::Vec3 computeRayIntersectionPoint(const osg::Vec3& a, const osg::Vec3& an, const osg::Vec3& c, const osg::Vec3& cn)
67    {
68        float denominator = ( cn.x() * an.y() - cn.y() * an.x());
69        if (denominator==0.0)
70        {
71            //OSG_NOTICE<<"computeRayIntersectionPoint()<<denominator==0.0"<<std::endl;
72            // line segments must be parallel.
73            return (a+c)*0.5;
74        }
75
76        float t = ((a.x()-c.x())*an.y() - (a.y()-c.y())*an.x()) / denominator;
77        return c + cn*t;
78    }
79
80    osg::Vec3 computeIntersectionPoint(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d)
81    {
82        return computeRayIntersectionPoint(a, b-a, c, d-c);
83    }
84
85    osg::Vec3 computeBisectorNormal(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d)
86    {
87        osg::Vec2 ab(a.x()-b.x(), a.y()-b.y());
88        osg::Vec2 dc(d.x()-c.x(), d.y()-c.y());
89        /*float length_ab =*/ ab.normalize();
90        /*float length_dc =*/ dc.normalize();
91
92        float e = dc.y() - ab.y();
93        float f = ab.x() - dc.x();
94        float denominator = sqrtf(e*e + f*f);
95        float nx = e / denominator;
96        float ny = f / denominator;
97        if (( ab.x()*ny - ab.y()*nx) > 0.0f)
98        {
99            // OSG_NOTICE<<"   computeBisectorNormal(a=["<<a<<"], b=["<<b<<"], c=["<<c<<"], d=["<<d<<"]), nx="<<nx<<", ny="<<ny<<", denominator="<<denominator<<" no need to swap"<<std::endl;
100            return osg::Vec3(nx,ny,0.0f);
101        }
102        else
103        {
104            OSG_NOTICE<<"   computeBisectorNormal(a=["<<a<<"], b=["<<b<<"], c=["<<c<<"], d=["<<d<<"]), nx="<<nx<<", ny="<<ny<<", denominator="<<denominator<<" need to swap!!!"<<std::endl;
105            return osg::Vec3(-nx,-ny,0.0f);
106        }
107    }
108
109    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)
110    {
111        osg::Vec3 intersection_abcd = computeIntersectionPoint(a,b,c,d);
112        osg::Vec3 bisector_abcd = computeBisectorNormal(a,b,c,d);
113        osg::Vec3 intersection_cdef = computeIntersectionPoint(c,d,e,f);
114        osg::Vec3 bisector_cdef = computeBisectorNormal(c,d,e,f);
115        if (bisector_abcd==bisector_cdef)
116        {
117            //OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
118            //OSG_NOTICE<<"   bisectors parallel, thickness = "<<FLT_MAX<<std::endl;
119            return FLT_MAX;
120        }
121
122        osg::Vec3 bisector_intersection = computeRayIntersectionPoint(intersection_abcd,bisector_abcd, intersection_cdef, bisector_cdef);
123        osg::Vec3 normal(d.y()-c.y(), c.x()-d.x(), 0.0);
124        float cd_length = normal.normalize();
125        if (cd_length==0)
126        {
127            //OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
128            //OSG_NOTICE<<"   segment length==0, thickness = "<<FLT_MAX<<std::endl;
129            return FLT_MAX;
130        }
131
132        float thickness = (bisector_intersection - c) * normal;
133    #if 0
134        OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
135        OSG_NOTICE<<"   bisector_abcd = "<<bisector_abcd<<", bisector_cdef="<<bisector_cdef<<std::endl;
136        OSG_NOTICE<<"   bisector_intersection = "<<bisector_intersection<<", thickness = "<<thickness<<std::endl;
137    #endif
138        return thickness;
139    }
140
141    float computeThickness(unsigned int i)
142    {
143        Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
144        Segment& seg_target = _segments[ (i) % _segments.size() ];
145        Segment& seg_after =  _segments[ (i+1) % _segments.size() ];
146        return computeBisectorIntersectorThickness(
147            (*_vertices)[seg_before.first], (*_vertices)[seg_before.second],
148            (*_vertices)[seg_target.first], (*_vertices)[seg_target.second],
149            (*_vertices)[seg_after.first], (*_vertices)[seg_after.second]);
150    }
151
152    void computeAllThickness()
153    {
154        for(unsigned int i=0; i<_segments.size(); ++i)
155        {
156            computeThickness(i);
157        }
158    }
159
160
161    bool findMinThickness(unsigned int& minThickness_i, float& minThickness)
162    {
163        minThickness_i = _segments.size();
164        for(unsigned int i=0; i<_segments.size(); ++i)
165        {
166            float thickness = computeThickness(i);
167            if (thickness>0.0 && thickness <  minThickness)
168            {
169                minThickness = thickness;
170                minThickness_i = i;
171            }
172        }
173
174        return minThickness_i != _segments.size();
175    }
176
177    void removeAllSegmentsBelowThickness(float targetThickness)
178    {
179        // OSG_NOTICE<<"removeAllSegmentsBelowThickness("<<targetThickness<<")"<<std::endl;
180        for(;;)
181        {
182            unsigned int minThickness_i = _segments.size();
183            float minThickness = targetThickness;
184            if (!findMinThickness(minThickness_i,minThickness)) break;
185
186            // OSG_NOTICE<<"  removing segment _segments["<<minThickness_i<<"] ("<<_segments[minThickness_i].first<<", "<<_segments[minThickness_i].second<<" with thickness="<<minThickness<<" "<<std::endl;
187            _segments.erase(_segments.begin()+minThickness_i);
188        }
189    }
190
191    osg::Vec3 computeBisectorPoint(unsigned int i, float targetThickness)
192    {
193        Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
194        Segment& seg_target = _segments[ (i) % _segments.size() ];
195        osg::Vec3& a = (*_vertices)[seg_before.first];
196        osg::Vec3& b = (*_vertices)[seg_before.second];
197        osg::Vec3& c = (*_vertices)[seg_target.first];
198        osg::Vec3& d = (*_vertices)[seg_target.second];
199        osg::Vec3 intersection_abcd = computeIntersectionPoint(a,b,c,d);
200        osg::Vec3 bisector_abcd = computeBisectorNormal(a,b,c,d);
201        osg::Vec3 ab_sidevector(b.y()-a.y(), a.x()-b.x(), 0.0);
202        ab_sidevector.normalize();
203        float scale_factor = 1.0/ (bisector_abcd*ab_sidevector);
204        osg::Vec3 new_vertex = intersection_abcd + bisector_abcd*(scale_factor*targetThickness);
205
206        // OSG_NOTICE<<"bisector_abcd = "<<bisector_abcd<<", ab_sidevector="<<ab_sidevector<<", b-a="<<b-a<<", scale_factor="<<scale_factor<<std::endl;
207
208        new_vertex.z() += 0.5f;
209        return new_vertex;
210    }
211
212    void addBoundaryToGeometry(osg::Geometry* geometry, float targetThickness)
213    {
214        if (_segments.empty()) return;
215
216        if (geometry->getVertexArray()==0) geometry->setVertexArray(new osg::Vec3Array);
217        osg::Vec3Array* new_vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
218
219        unsigned int first = new_vertices->size();
220        unsigned int count = 0;
221
222        // reserve enough space in the vertex array to accomodate the vertices associated with the segments
223        // new_vertices->reserve(new_vertices->size()+_segments.size()+1);
224
225        // create vertices
226        unsigned int previous_second = _segments[0].second;
227        osg::Vec3 newPoint = computeBisectorPoint(0, targetThickness);
228        new_vertices->push_back(newPoint);
229        ++count;
230
231        for(unsigned int i=1; i<_segments.size(); ++i)
232        {
233            previous_second = _segments[i].second;
234            newPoint = computeBisectorPoint(i, targetThickness);
235            new_vertices->push_back(newPoint);
236            ++count;
237        }
238
239        // repeat the first point to make it a full closed loop
240        new_vertices->push_back((*new_vertices)[first]);
241        ++count;
242
243        // add DrawArrays primitive set for polygon
244        if (count!=0) geometry->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, first, count));
245    }
246
247};
248
249
250osg::Geometry* computeThickness(osg::Geometry* orig_geometry, float thickness)
251{
252    // OSG_NOTICE<<"computeThickness("<<orig_geometry<<")"<<std::endl;
253    osg::Vec3Array* orig_vertices = dynamic_cast<osg::Vec3Array*>(orig_geometry->getVertexArray());
254    osg::Geometry::PrimitiveSetList& orig_primitives = orig_geometry->getPrimitiveSetList();
255
256    osg::Geometry* new_geometry = new osg::Geometry;
257
258    osg::Vec4Array* new_colours = new osg::Vec4Array;
259    new_colours->push_back(osg::Vec4(1.0,1.0,0.0,1.0));
260    new_geometry->setColorArray(new_colours);
261    new_geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
262
263    for(osg::Geometry::PrimitiveSetList::iterator itr = orig_primitives.begin();
264        itr != orig_primitives.end();
265        ++itr)
266    {
267        osg::DrawArrays* drawArray = dynamic_cast<osg::DrawArrays*>(itr->get());
268        if (drawArray && drawArray->getMode()==GL_POLYGON)
269        {
270            Boundary boundary(orig_vertices, drawArray->getFirst(), drawArray->getCount());
271            boundary.computeAllThickness();
272
273            boundary.removeAllSegmentsBelowThickness(thickness);
274            boundary.addBoundaryToGeometry(new_geometry, thickness);
275        }
276    }
277    return new_geometry;
278}
279
280int main(int argc, char** argv)
281{
282    osg::ArgumentParser arguments(&argc, argv);
283
284    if (arguments.read("--test"))
285    {
286        return main_test(argc,argv);
287    }
288    else if (arguments.read("--original") || arguments.read("--orig"))
289    {
290        return main_orig(argc,argv);
291    }
292
293    std::string fontFile("arial.ttf");
294    while(arguments.read("-f",fontFile)) {}
295
296    std::string word("This is a simple test");
297
298    while(arguments.read("--ascii"))
299    {
300        word.clear();
301        for(unsigned int c=' '; c<=127;++c)
302        {
303            word.push_back(c);
304        }
305    }
306
307    while(arguments.read("-w",word)) {}
308
309    osg::ref_ptr<osgText::Font3D> font = osgText::readFont3DFile(fontFile);
310    if (!font) return 1;
311    OSG_NOTICE<<"Read font "<<fontFile<<" font="<<font.get()<<std::endl;
312
313    bool useTessellator = false;
314    while(arguments.read("-t") || arguments.read("--tessellate")) { useTessellator = true; }
315
316    float thickness = 5.0;
317    while(arguments.read("--thickness",thickness)) {}
318
319    osg::ref_ptr<osg::Group> group = new osg::Group;
320    osg::Vec3 position;
321
322    for(unsigned int i=0; i<word.size(); ++i)
323    {
324        osg::ref_ptr<osgText::Font3D::Glyph3D> glyph = font->getGlyph(word[i]);
325        if (!glyph) return 1;
326
327        osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
328        transform->setPosition(position);
329        transform->setAttitude(osg::Quat(osg::inDegrees(90.0),osg::Vec3d(1.0,0.0,0.0)));
330
331        position.x() += glyph->getHorizontalWidth();
332
333        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
334
335        osg::Vec3Array* vertices = glyph->getRawVertexArray();
336        osg::Geometry::PrimitiveSetList& primitives = glyph->getRawFacePrimitiveSetList();
337
338        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
339        geometry->setVertexArray(vertices);
340        geometry->setPrimitiveSetList(primitives);
341        osg::Vec4Array* colours = new osg::Vec4Array;
342        colours->push_back(osg::Vec4(1.0,1.0,1.0,1.0));
343        geometry->setColorArray(colours);
344        geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
345
346        osg::Geometry* bevel = computeThickness(geometry, thickness);
347
348        if (bevel) geode->addDrawable(bevel);
349
350        if (useTessellator)
351        {
352            if (geometry)
353            {
354                osgUtil::Tessellator ts;
355                ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
356                ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
357                ts.retessellatePolygons(*geometry);
358            }
359
360            if (bevel)
361            {
362                osgUtil::Tessellator ts;
363                ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
364                ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
365                ts.retessellatePolygons(*bevel);
366            }
367
368        }
369
370        geode->addDrawable(geometry.get());
371
372        transform->addChild(geode.get());
373
374        group->addChild(transform.get());
375    }
376
377    std::string filename;
378    if (arguments.read("-o", filename)) osgDB::writeNodeFile(*group, filename);
379
380    osgViewer::Viewer viewer(arguments);
381    viewer.setSceneData(group.get());
382    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
383    return viewer.run();
384}
Note: See TracBrowser for help on using the browser.