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 "GlyphGeometry.h"
15
16 #include <osg/io_utils>
17 #include <osg/TriangleIndexFunctor>
18 #include <osg/LineWidth>
19 #include <osgUtil/Tessellator>
20 #include <osg/CullFace>
21 #include <osgDB/WriteFile>
22
23 #include <limits.h>
24
25
26 #define REPORT_TIME 0
27
28 #if REPORT_TIME
29 #include <osg/Timer>
30 #endif
31
32 namespace osgText
33 {
34
35 /////////////////////////////////////////////////////////////////////////////////////////
36 //
37 // Boundary
38 //
39 class Boundary : public osg::Referenced
40 {
41 public:
42
43 struct Segment
44 {
SegmentosgText::Boundary::Segment45 Segment(unsigned int f, unsigned int s, float t):
46 first(f), second(s), thickness(t), suggestedThickness(t) {}
47
SegmentosgText::Boundary::Segment48 Segment(const Segment& seg):
49 first(seg.first),
50 second(seg.second),
51 thickness(seg.thickness),
52 suggestedThickness(seg.suggestedThickness) {}
53
operator =osgText::Boundary::Segment54 Segment& operator = (const Segment& seg)
55 {
56 first = seg.first;
57 second = seg.second;
58 thickness = seg.thickness;
59 suggestedThickness = seg.suggestedThickness;
60 return *this;
61 }
62
63 unsigned int first;
64 unsigned int second;
65 float thickness;
66 float suggestedThickness;
67 };
68
69
70 //typedef std::pair<unsigned int, unsigned int> Segment;
71 typedef std::vector<Segment> Segments;
72 osg::ref_ptr<const osg::Vec3Array> _vertices;
73 osg::ref_ptr<const osg::DrawElementsUShort> _elements;
74 Segments _segments;
75 bool verbose;
76
Boundary(const osg::Vec3Array * vertices,const osg::PrimitiveSet * primitiveSet,float thickness)77 Boundary(const osg::Vec3Array* vertices, const osg::PrimitiveSet* primitiveSet, float thickness):
78 verbose(false)
79 {
80 const osg::DrawArrays* drawArrays = dynamic_cast<const osg::DrawArrays*>(primitiveSet);
81 if (drawArrays)
82 {
83 set(vertices, drawArrays->getFirst(), drawArrays->getCount(), thickness);
84 }
85 else
86 {
87 const osg::DrawElementsUShort* elements = dynamic_cast<const osg::DrawElementsUShort*>(primitiveSet);
88 if (elements) set(vertices, elements, thickness);
89 }
90 }
91
set(const osg::Vec3Array * vertices,unsigned int start,unsigned int count,float thickness)92 void set(const osg::Vec3Array* vertices, unsigned int start, unsigned int count, float thickness)
93 {
94 osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::POLYGON);
95 for(unsigned int i=start; i<start+count; ++i)
96 {
97 elements->push_back(i);
98 }
99
100 set(vertices, elements, thickness);
101 }
102
set(const osg::Vec3Array * vertices,const osg::DrawElementsUShort * elements,float thickness)103 void set(const osg::Vec3Array* vertices, const osg::DrawElementsUShort* elements, float thickness)
104 {
105 _vertices = vertices;
106 _elements = elements;
107
108 _segments.clear();
109
110 if (elements->empty()) return;
111
112 _segments.reserve(elements->size()-1);
113 for(unsigned int i=0; i<elements->size()-1; ++i)
114 {
115 _segments.push_back( Segment((*elements)[i], (*elements)[i+1], thickness) );
116 }
117 }
118
shorter(float original_thickness,float new_thickness) const119 bool shorter(float original_thickness, float new_thickness) const { return (original_thickness<0.0f) ? (new_thickness>original_thickness) : (new_thickness<original_thickness); }
120
shorten(float & original_thickness,float new_thickness)121 bool shorten(float& original_thickness, float new_thickness) { if (shorter(original_thickness, new_thickness)) { original_thickness = new_thickness; return true; } else return false; }
122
shortenBisector(unsigned int i,float new_thickness)123 bool shortenBisector(unsigned int i, float new_thickness)
124 {
125 bool r1 = shorten(_segments[(i+_segments.size()-1)%(_segments.size())].suggestedThickness, new_thickness);
126 bool r2 = shorten(_segments[i].suggestedThickness, new_thickness);
127 return r1 || r2;
128 }
129
applySuggestedThickness()130 void applySuggestedThickness()
131 {
132 for(Segments::iterator itr = _segments.begin();
133 itr != _segments.end();
134 ++itr)
135 {
136 (*itr).thickness = (*itr).suggestedThickness;
137 }
138 }
139
applyThickness(float thickness)140 void applyThickness(float thickness)
141 {
142 for(Segments::iterator itr = _segments.begin();
143 itr != _segments.end();
144 ++itr)
145 {
146 (*itr).thickness = thickness;
147 (*itr).suggestedThickness = thickness;
148 }
149 }
150
getSuggestedThicknessRange(float & smallest,float & largest)151 void getSuggestedThicknessRange(float& smallest, float& largest)
152 {
153 for(Segments::iterator itr = _segments.begin();
154 itr != _segments.end();
155 ++itr)
156 {
157 float t = (*itr).suggestedThickness;
158 if (t<smallest) smallest = t;
159 if (t>largest) largest = t;
160 }
161
162 if (largest<0.0f) std::swap(smallest, largest);
163 }
164
computeRayIntersectionPoint(const osg::Vec3 & a,const osg::Vec3 & an,const osg::Vec3 & c,const osg::Vec3 & cn)165 osg::Vec3 computeRayIntersectionPoint(const osg::Vec3& a, const osg::Vec3& an, const osg::Vec3& c, const osg::Vec3& cn)
166 {
167 float denominator = ( cn.x() * an.y() - cn.y() * an.x());
168 if (denominator==0.0f)
169 {
170 //OSG_NOTICE<<"computeRayIntersectionPoint()<<denominator==0.0"<<std::endl;
171 // line segments must be parallel.
172 return (a+c)*0.5f;
173 }
174
175 float t = ((a.x()-c.x())*an.y() - (a.y()-c.y())*an.x()) / denominator;
176 return c + cn*t;
177 }
178
computeIntersectionPoint(const osg::Vec3 & a,const osg::Vec3 & b,const osg::Vec3 & c,const osg::Vec3 & d)179 osg::Vec3 computeIntersectionPoint(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d)
180 {
181 return computeRayIntersectionPoint(a, b-a, c, d-c);
182 }
183
computeBisectorNormal(const osg::Vec3 & a,const osg::Vec3 & b,const osg::Vec3 & c,const osg::Vec3 & d)184 osg::Vec3 computeBisectorNormal(const osg::Vec3& a, const osg::Vec3& b, const osg::Vec3& c, const osg::Vec3& d)
185 {
186 osg::Vec2 ab(a.x()-b.x(), a.y()-b.y());
187 osg::Vec2 dc(d.x()-c.x(), d.y()-c.y());
188 /*float length_ab =*/ ab.normalize();
189 /*float length_dc =*/ dc.normalize();
190
191 float e = dc.y() - ab.y();
192 float f = ab.x() - dc.x();
193 float denominator = sqrtf(e*e + f*f);
194 float nx = e / denominator;
195 float ny = f / denominator;
196 if (( ab.x()*ny - ab.y()*nx) > 0.0f)
197 {
198 // OSG_NOTICE<<" computeBisectorNormal(a=["<<a<<"], b=["<<b<<"], c=["<<c<<"], d=["<<d<<"]), nx="<<nx<<", ny="<<ny<<", denominator="<<denominator<<" no need to swap"<<std::endl;
199 return osg::Vec3(nx,ny,0.0f);
200 }
201 else
202 {
203 OSG_INFO<<" computeBisectorNormal(a=["<<a<<"], b=["<<b<<"], c=["<<c<<"], d=["<<d<<"]), nx="<<nx<<", ny="<<ny<<", denominator="<<denominator<<" need to swap!!!"<<std::endl;
204 return osg::Vec3(-nx,-ny,0.0f);
205 }
206 }
207
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)208 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)
209 {
210 osg::Vec3 intersection_abcd = computeIntersectionPoint(a,b,c,d);
211 osg::Vec3 bisector_abcd = computeBisectorNormal(a,b,c,d);
212 osg::Vec3 intersection_cdef = computeIntersectionPoint(c,d,e,f);
213 osg::Vec3 bisector_cdef = computeBisectorNormal(c,d,e,f);
214 if (bisector_abcd==bisector_cdef)
215 {
216 //OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
217 //OSG_NOTICE<<" bisectors parallel, thickness = "<<FLT_MAX<<std::endl;
218 return FLT_MAX;
219 }
220
221 osg::Vec3 bisector_intersection = computeRayIntersectionPoint(intersection_abcd,bisector_abcd, intersection_cdef, bisector_cdef);
222 osg::Vec3 normal(d.y()-c.y(), c.x()-d.x(), 0.0);
223 float cd_length = normal.normalize();
224 if (cd_length==0)
225 {
226 //OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
227 //OSG_NOTICE<<" segment length==0, thickness = "<<FLT_MAX<<std::endl;
228 return FLT_MAX;
229 }
230
231 float thickness = (bisector_intersection - c) * normal;
232 #if 0
233 OSG_NOTICE<<"computeBisectorIntersector(["<<a<<"], ["<<b<<"], ["<<c<<"], ["<<d<<"], ["<<e<<"], ["<<f<<"[)"<<std::endl;
234 OSG_NOTICE<<" bisector_abcd = "<<bisector_abcd<<", bisector_cdef="<<bisector_cdef<<std::endl;
235 OSG_NOTICE<<" bisector_intersection = "<<bisector_intersection<<", thickness = "<<thickness<<std::endl;
236 #endif
237 return thickness;
238 }
239
240
computeThickness(unsigned int i)241 float computeThickness(unsigned int i)
242 {
243 Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
244 Segment& seg_target = _segments[ (i) % _segments.size() ];
245 Segment& seg_after = _segments[ (i+1) % _segments.size() ];
246 return computeBisectorIntersectorThickness(
247 (*_vertices)[seg_before.first], (*_vertices)[seg_before.second],
248 (*_vertices)[seg_target.first], (*_vertices)[seg_target.second],
249 (*_vertices)[seg_after.first], (*_vertices)[seg_after.second]);
250 }
251
252
findMinThickness(unsigned int & minThickness_i,float & minThickness)253 bool findMinThickness(unsigned int& minThickness_i, float& minThickness)
254 {
255 minThickness_i = _segments.size();
256 for(unsigned int i=0; i<_segments.size(); ++i)
257 {
258 float thickness = computeThickness(i);
259 if (thickness>0.0 && thickness < minThickness)
260 {
261 minThickness = thickness;
262 minThickness_i = i;
263 }
264 }
265
266 return minThickness_i != _segments.size();
267 }
268
removeAllSegmentsBelowThickness(float targetThickness)269 void removeAllSegmentsBelowThickness(float targetThickness)
270 {
271 // OSG_NOTICE<<"removeAllSegmentsBelowThickness("<<targetThickness<<")"<<std::endl;
272 for(;;)
273 {
274 unsigned int minThickness_i = _segments.size();
275 float minThickness = targetThickness;
276 if (!findMinThickness(minThickness_i,minThickness)) break;
277
278 // OSG_NOTICE<<" removing segment _segments["<<minThickness_i<<"] ("<<_segments[minThickness_i].first<<", "<<_segments[minThickness_i].second<<" with thickness="<<minThickness<<" "<<std::endl;
279 _segments.erase(_segments.begin()+minThickness_i);
280 }
281 }
282
findMaxThickness(unsigned int & maxThickness_i,float & maxThickness)283 bool findMaxThickness(unsigned int& maxThickness_i, float& maxThickness)
284 {
285 maxThickness_i = _segments.size();
286 for(unsigned int i=0; i<_segments.size(); ++i)
287 {
288 float thickness = computeThickness(i);
289 if (thickness<0.0 && thickness > maxThickness)
290 {
291 maxThickness = thickness;
292 maxThickness_i = i;
293 }
294 }
295
296 return maxThickness_i != _segments.size();
297 }
298
299
removeAllSegmentsAboveThickness(float targetThickness)300 void removeAllSegmentsAboveThickness(float targetThickness)
301 {
302 // OSG_NOTICE<<"removeAllSegmentsBelowThickness("<<targetThickness<<")"<<std::endl;
303 for(;;)
304 {
305 unsigned int maxThickness_i = _segments.size();
306 float maxThickness = targetThickness;
307 if (!findMaxThickness(maxThickness_i,maxThickness)) break;
308
309 // OSG_NOTICE<<" removing segment _segments["<<minThickness_i<<"] ("<<_segments[minThickness_i].first<<", "<<_segments[minThickness_i].second<<" with thickness="<<minThickness<<" "<<std::endl;
310 _segments.erase(_segments.begin()+maxThickness_i);
311 }
312 }
313
computeBisectorPoint(unsigned int i,float targetThickness,osg::Vec3 & va,osg::Vec3 & vb)314 float computeBisectorPoint(unsigned int i, float targetThickness, osg::Vec3& va, osg::Vec3& vb)
315 {
316 Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
317 Segment& seg_target = _segments[ (i) % _segments.size() ];
318 const osg::Vec3& a = (*_vertices)[seg_before.first];
319 const osg::Vec3& b = (*_vertices)[seg_before.second];
320 const osg::Vec3& c = (*_vertices)[seg_target.first];
321 const osg::Vec3& d = (*_vertices)[seg_target.second];
322 osg::Vec3 intersection_abcd = computeIntersectionPoint(a,b,c,d);
323 osg::Vec3 bisector_abcd = computeBisectorNormal(a,b,c,d);
324 osg::Vec3 ab_sidevector(b.y()-a.y(), a.x()-b.x(), 0.0);
325 ab_sidevector.normalize();
326 float scale_factor = 1.0/ (bisector_abcd*ab_sidevector);
327 float new_thickness = scale_factor*targetThickness;
328 osg::Vec3 new_vertex = intersection_abcd + bisector_abcd*new_thickness;
329
330 //OSG_NOTICE<<"computeBisectorPoint("<<i<<", targetThickness="<<targetThickness<<", bisector_abcd = "<<bisector_abcd<<", ab_sidevector="<<ab_sidevector<<", b-a="<<b-a<<", scale_factor="<<scale_factor<<std::endl;
331
332 va = intersection_abcd;
333 vb = new_vertex;
334
335 return new_thickness;
336 }
337
computeBisectorPoint(unsigned int i,osg::Vec3 & va,osg::Vec3 & vb)338 float computeBisectorPoint(unsigned int i, osg::Vec3& va, osg::Vec3& vb)
339 {
340 float tbefore = _segments[(i+_segments.size()-1)% _segments.size()].thickness;
341 float tafter = _segments[(i+_segments.size())% _segments.size()].thickness;
342 float t = tafter<0.0 ? osg::maximum(tbefore, tafter) : osg::minimum(tbefore, tafter);
343 return computeBisectorPoint(i, t, va, vb);
344 }
345
computeThicknessThatBisectorAndSegmentMeet(const osg::Vec3 & va,const osg::Vec3 & vb,unsigned int bi,float original_thickness)346 float computeThicknessThatBisectorAndSegmentMeet(const osg::Vec3& va, const osg::Vec3& vb, unsigned int bi, float original_thickness)
347 {
348 osg::Vec3 bisector = (vb-va);
349
350 bisector /= original_thickness;
351
352 Segment& seg_opposite = _segments[ (bi+_segments.size()) % _segments.size() ];
353 const osg::Vec3& vc = (*_vertices)[seg_opposite.first];
354 const osg::Vec3& vd = (*_vertices)[seg_opposite.second];
355 osg::Vec3 cdn(vd.y()-vc.y(), vc.x()-vd.x(), 0.0);
356
357 if (cdn.normalize()==0.0f) return false;
358
359
360 float denom = ( 1.0f - (bisector * cdn));
361 if (denom==0.0f)
362 {
363 return FLT_MAX;
364 }
365
366 float h = ((va-vc)*cdn) / denom;
367 if (h<0.0)
368 {
369 return FLT_MAX;
370 }
371
372 return h;
373 }
374
clampSegmentToEdge(osg::Vec3 & va,osg::Vec3 & vb,const osg::Vec3 & vc,const osg::Vec3 & vd)375 int clampSegmentToEdge(osg::Vec3& va, osg::Vec3& vb, const osg::Vec3& vc, const osg::Vec3& vd)
376 {
377 osg::Vec2 ncd(vc.y()-vd.y(), vd.x()-vc.x());
378 float na = (va.x()-vc.x())*ncd.x() + (va.y()-vc.y())*ncd.y();
379 float nb = (vb.x()-vc.x())*ncd.x() + (vb.y()-vc.y())*ncd.y();
380
381 if (na>=0.0f) // check if na is inside
382 {
383 // check if wholly inside
384 if (nb>=0.0f) return 1;
385
386 // na is inside, nb outside, need to shift nb to (vc, vd) line.
387 float d = na-nb;
388
389 if (d==0.0f)
390 {
391 return 1;
392 }
393
394 float r = na/d;
395 vb = va+(vb-va)*r;
396 }
397 else // na<0.0 and therefore outside
398 {
399 // check if wholly outside
400 if (nb<=0.0f) return -1;
401
402 // na is outside, nb inside, need to shift na to (vc, vd) line.
403 float d = nb-na;
404 if (d==0.0f)
405 {
406 return -1;
407 }
408
409 float r = -na/d;
410
411 va = va+(vb-va)*r;
412 }
413
414 return 0;
415 }
416
417
doesSegmentIntersectQuad(osg::Vec3 va,osg::Vec3 vb,const osg::Vec3 & v1,const osg::Vec3 & v2,const osg::Vec3 & v3,const osg::Vec3 & v4)418 bool doesSegmentIntersectQuad(osg::Vec3 va, osg::Vec3 vb, const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3, const osg::Vec3& v4)
419 {
420 osg::Vec2 ncd(v1.y()-v2.y(), v2.x()-v1.x());
421
422 // catch case of bisector boundary point being behind the segment of interest
423 float na = (va.x()-v1.x())*ncd.x() + (va.y()-v1.y())*ncd.y();
424 if (na>=0.0) return false;
425
426 // catch case of bisector pointing away from the segment of interest
427 float nb = (vb.x()-v1.x())*ncd.x() + (vb.y()-v1.y())*ncd.y();
428 if (na>=nb) return false;
429
430 osg::Vec3 cp123 = (v2-v1)^(v3-v2);
431 osg::Vec3 cp234 = (v3-v2)^(v4-v3);
432 float dot_1234 = cp123*cp234;
433 bool not_crossed_over = (dot_1234>=0.0);
434
435 if (clampSegmentToEdge(va, vb, v1, v4)<0) return false;
436 if (clampSegmentToEdge(va, vb, v3, v2)<0) return false;
437 if (clampSegmentToEdge(va, vb, v2, v1)<0) return false;
438 if (not_crossed_over && clampSegmentToEdge(va, vb, v4, v3)<0) return false;
439
440 return true;
441 }
442
checkBisectorAgainstBoundary(osg::Vec3 va,osg::Vec3 vb,float original_thickness)443 float checkBisectorAgainstBoundary(osg::Vec3 va, osg::Vec3 vb, float original_thickness)
444 {
445 float thickness = original_thickness;
446
447 osg::Vec3 before_outer, before_inner;
448 osg::Vec3 after_outer, after_inner;
449
450 for(unsigned int i=0; i<_segments.size(); ++i)
451 {
452 Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ];
453 Segment& seg_target = _segments[ (i) % _segments.size() ];
454 Segment& seg_after = _segments[ (i+1) % _segments.size() ];
455
456 computeBisectorPoint(i, before_outer, before_inner);
457 computeBisectorPoint(i+1, after_outer, after_inner);
458
459 if (doesSegmentIntersectQuad(va, vb, before_outer, after_outer, after_inner, before_inner))
460 {
461 float new_thickness = computeThicknessThatBisectorAndSegmentMeet(va, vb, i, original_thickness);
462
463 shorten(thickness, new_thickness);
464 shorten(seg_before.suggestedThickness, new_thickness);
465 shorten(seg_target.suggestedThickness, new_thickness);
466 shorten(seg_after.suggestedThickness, new_thickness);
467 }
468
469 }
470 return thickness;
471 }
472
checkBoundaries(Boundary & boundary)473 void checkBoundaries(Boundary& boundary)
474 {
475 osg::Vec3 va, vb;
476 float min_thickiness = FLT_MAX;
477 for(unsigned int i=0; i<boundary._segments.size(); ++i)
478 {
479 boundary.computeBisectorPoint(i, va, vb);
480 float new_thickness = checkBisectorAgainstBoundary(va, vb, boundary._segments[i].thickness);
481 if (boundary.shortenBisector(i, new_thickness))
482 {
483 shorten (min_thickiness, new_thickness);
484 }
485
486 }
487 }
488
addBoundaryToGeometry(osg::Geometry * geometry,float,const std::string & faceName,const std::string & bevelName)489 void addBoundaryToGeometry(osg::Geometry* geometry, float /*targetThickness*/, const std::string& faceName, const std::string& bevelName)
490 {
491 if (_segments.empty()) return;
492
493 unsigned int start = (*_elements)[0];
494 unsigned int count = _elements->size();
495
496 osg::Vec3Array* new_vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
497 if (!new_vertices)
498 {
499 new_vertices = new osg::Vec3Array(*_vertices);
500 geometry->setVertexArray(new_vertices);
501 }
502
503 // allocate the primitive set to store the face geometry
504 osg::ref_ptr<osg::DrawElementsUShort> face = new osg::DrawElementsUShort(GL_POLYGON);
505 face->setName(faceName);
506
507 // reserve enough space in the vertex array to accommodate the vertices associated with the segments
508 new_vertices->reserve(new_vertices->size() + _segments.size()+1 + count);
509
510 // create vertices
511 unsigned int previous_second = _segments[0].second;
512
513 osg::Vec3 boundaryPoint, newPoint;
514 computeBisectorPoint(0, boundaryPoint, newPoint);
515
516 unsigned int first = new_vertices->size();
517 new_vertices->push_back(newPoint);
518
519 if (_segments[0].first != start)
520 {
521 //OSG_NOTICE<<"We have pruned from the start"<<std::endl;
522 for(unsigned int j=start; j<=_segments[0].first;++j)
523 {
524 face->push_back(first);
525 }
526 }
527 else
528 {
529 face->push_back(first);
530 }
531
532
533 for(unsigned int i=1; i<_segments.size(); ++i)
534 {
535
536 computeBisectorPoint(i, boundaryPoint, newPoint);
537
538 unsigned int vi = new_vertices->size();
539 new_vertices->push_back(newPoint);
540
541 if (previous_second != _segments[i].first)
542 {
543 //OSG_NOTICE<<"Gap in boundary"<<previous_second<<" to "<<_segments[i].first<<std::endl;
544 for(unsigned int j=previous_second; j<=_segments[i].first;++j)
545 {
546 face->push_back(vi);
547 }
548 }
549 else
550 {
551 face->push_back(vi);
552 }
553
554 previous_second = _segments[i].second;
555 }
556
557 // fill the end of the polygon with repititions of the first index in the polygon to ensure
558 // that the original and new boundary polygons have the same number and pairing of indices.
559 // This ensures that the bevel can be created coherently.
560 while(face->size() < count)
561 {
562 face->push_back(first);
563 }
564
565 if (!faceName.empty())
566 {
567 // add face primitive set for polygon
568 geometry->addPrimitiveSet(face.get());
569 }
570
571 osg::DrawElementsUShort* bevel = new osg::DrawElementsUShort(GL_QUAD_STRIP);
572 bevel->setName(bevelName);
573 bevel->reserve(count*2);
574 for(unsigned int i=0; i<count; ++i)
575 {
576 bevel->push_back((*_elements)[i]);
577 bevel->push_back((*face)[i]);
578 }
579 geometry->addPrimitiveSet(bevel);
580 }
581
582 protected:
583
~Boundary()584 virtual ~Boundary() {}
585 };
586
587
588 /////////////////////////////////////////////////////////////////////////////////////////
589 //
590 // computeGlyphGeometry
591 //
592 struct CollectTriangleIndicesFunctor
593 {
CollectTriangleIndicesFunctorosgText::CollectTriangleIndicesFunctor594 CollectTriangleIndicesFunctor() {}
595
596 typedef std::vector<unsigned int> Indices;
597 Indices _indices;
598
operator ()osgText::CollectTriangleIndicesFunctor599 void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
600 {
601 if (p1==p2 || p2==p3 || p1==p3)
602 {
603 return;
604 }
605
606 _indices.push_back(p1);
607 _indices.push_back(p3);
608 _indices.push_back(p2);
609
610 }
611 };
612
613
614
computeGlyphGeometry(const osgText::Glyph3D * glyph,const Bevel & bevel,float shellThickness)615 OSGTEXT_EXPORT osg::Geometry* computeGlyphGeometry(const osgText::Glyph3D* glyph, const Bevel& bevel, float shellThickness)
616 {
617 #if REPORT_TIME
618 osg::ElapsedTime timer;
619 #endif
620
621 float bevelThickness = bevel.getBevelThickness();
622 bool roundedWebs = bevel.getSmoothConcaveJunctions();
623
624
625 const osg::Vec3Array* source_vertices = glyph->getRawVertexArray();
626 const osg::Geometry::PrimitiveSetList& source_primitives = glyph->getRawFacePrimitiveSetList();
627
628 if (!source_vertices) return NULL;
629 if (source_primitives.empty()) return NULL;
630
631 #if 0
632 {
633 osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
634 geom->setVertexArray(const_cast<osg::Vec3Array*>(source_vertices));
635 geom->setPrimitiveSetList(source_primitives);
636
637 osgDB::writeNodeFile(*geom, "test.osgt");
638 }
639 #endif
640
641
642 osg::ref_ptr<osg::Vec3Array> orig_vertices = new osg::Vec3Array;
643 osg::Geometry::PrimitiveSetList orig_primitives;
644
645 orig_vertices->reserve(source_vertices->size());
646 orig_primitives.reserve(source_primitives.size());
647
648 typedef std::vector<int> Indices;
649 Indices remappedIndices(source_vertices->size(), -1);
650
651 float convexCornerInsertionWidth = 0.02;
652 float convexCornerCutoffAngle = osg::inDegrees(45.0f);
653
654 int num_vertices_on_web = 10;
655
656
657 for(osg::Geometry::PrimitiveSetList::const_iterator itr = source_primitives.begin();
658 itr != source_primitives.end();
659 ++itr)
660 {
661 const osg::DrawElementsUShort* elements = dynamic_cast<const osg::DrawElementsUShort*>(itr->get());
662 if (elements && elements->size()>2)
663 {
664 osg::ref_ptr<osg::DrawElementsUShort> new_elements = new osg::DrawElementsUShort(elements->getMode());
665 orig_primitives.push_back(new_elements.get());
666
667 int num_indices = elements->size();
668 for(int ei = 0; ei<num_indices-1; ++ei)
669 {
670 int vi_before = (ei==0) ? (*elements)[(elements->size()-2)] : (*elements)[ei-1];
671 int vi_curr = (*elements)[ei];
672 int vi_after = (*elements)[ei+1];
673
674 osg::Vec3 va = (*source_vertices)[vi_before];
675 osg::Vec3 vb = (*source_vertices)[vi_curr];
676 osg::Vec3 vc = (*source_vertices)[vi_after];
677
678 // OSG_NOTICE<<" "<<vi_before<<", "<<vi_curr<<", "<<vi_after<<std::endl;
679
680 if (vi_curr>=static_cast<int>(remappedIndices.size())) remappedIndices.resize(vi_curr+1,-1);
681
682 int new_index = remappedIndices[vi_curr];
683 if (new_index<0)
684 {
685 remappedIndices[vi_curr] = new_index = orig_vertices->size();
686 orig_vertices->push_back(vb);
687 }
688 new_elements->push_back(new_index);
689
690 if (roundedWebs)
691 {
692 osg::Vec3 vab = vb-va;
693 osg::Vec3 vbc = vc-vb;
694 float len_vab = vab.normalize();
695 float len_vbc = vbc.normalize();
696
697 if (len_vab*len_vbc==0.0f)
698 {
699 OSG_NOTICE<<"Warning: len_vab="<<len_vab<<", len_vbc="<<len_vbc<<std::endl;
700 continue;
701 }
702
703 osg::Vec3 cross = vab ^ vbc;
704 if (cross.z()>0.0)
705 {
706 float dot = vab * vbc;
707
708 float theta = atan2(cross.z(), dot);
709
710 // OSG_NOTICE<<" convex segment vab=("<<vab<<") vbc=("<<vbc<<") theta"<<osg::RadiansToDegrees(theta)<<std::endl;
711
712 if (theta>convexCornerCutoffAngle)
713 {
714 vab.normalize();
715 vbc.normalize();
716
717 float min_len = osg::minimum(len_vab, len_vbc);
718
719 osg::Vec3 v_before = vb - vab*(min_len*convexCornerInsertionWidth);
720 osg::Vec3 v_after = vb + vbc*(min_len*convexCornerInsertionWidth);
721 osg::Vec3 v_mid = v_before + (v_after-v_before)*0.5f;
722
723 float h = (vb-v_mid).length();
724 float w = (v_after-v_before).length()*0.5f;
725 float l = w*w / h;
726 float r = sqrt(l*l + w*w);
727 float alpha = atan2(w,h);
728 float beta = osg::PI-alpha*2.0f;
729
730 // OSG_NOTICE<<" h = "<<h<<", w = "<<w<<", l = "<<l<<", r="<<r<<", alpha="<<osg::RadiansToDegrees(alpha)<<", beta="<<osg::RadiansToDegrees(beta)<<std::endl;
731
732 osg::Vec3 vertical = (vb-v_mid)*(1.0f/h);
733 osg::Vec3 horizontal = (v_after-v_before)*(r/(w*2.0f));
734
735 vertical.normalize();
736
737 osg::Vec3 v_center = v_mid-vertical*l;
738 vertical *= r;
739
740 (*orig_vertices)[new_index] = v_before;
741
742 for(int i=1; i<=num_vertices_on_web-1; ++i)
743 {
744 float gamma = ((static_cast<float>(i)/static_cast<float>(num_vertices_on_web))-0.5f) * beta;
745 // OSG_NOTICE<<" gamma = "<<osg::RadiansToDegrees(gamma)<<" sin(gamma)="<<sin(gamma)<<", cos(gamma)="<<cos(gamma)<<std::endl;
746
747 osg::Vec3 v = v_center + horizontal*sin(gamma) + vertical*cos(gamma);
748
749 new_elements->push_back(orig_vertices->size());
750 orig_vertices->push_back(v);
751 }
752 new_elements->push_back(orig_vertices->size());
753 orig_vertices->push_back(v_after);
754 }
755 }
756 }
757 }
758
759 // add the first element to create the loop.
760 new_elements->push_back(new_elements->front());
761
762 }
763
764 }
765
766 if (!orig_vertices) return 0;
767 if (orig_primitives.empty()) return 0;
768
769
770
771 osg::ref_ptr<osg::Geometry> new_geometry = new osg::Geometry;
772
773 #define HANDLE_SHELL 0
774
775
776 typedef std::vector< osg::ref_ptr<Boundary> > Boundaries;
777 Boundaries innerBoundaries;
778 Boundaries outerBoundaries;
779
780 for(osg::Geometry::PrimitiveSetList::const_iterator itr = orig_primitives.begin();
781 itr != orig_primitives.end();
782 ++itr)
783 {
784 if ((*itr)->getMode()==GL_POLYGON)
785 {
786 osg::ref_ptr<Boundary> boundaryInner = new Boundary(orig_vertices.get(), itr->get(), bevelThickness);
787 //boundaryInner->removeAllSegmentsBelowThickness(bevelThickness);
788 innerBoundaries.push_back(boundaryInner.get());
789 #if HANDLE_SHELL
790 osg::ref_ptr<Boundary> boundaryOuter = new Boundary(orig_vertices.get(), itr->get(), -shellThickness);
791 boundaryOuter->removeAllSegmentsAboveThickness(-shellThickness);
792 outerBoundaries.push_back(boundaryOuter.get());
793 #endif
794 }
795 }
796
797 OSG_INFO<<"Handling bevel"<<std::endl;
798 for(unsigned int i=0; i<innerBoundaries.size(); ++i)
799 {
800 for(unsigned int j=0; j<innerBoundaries.size(); ++j)
801 {
802 innerBoundaries[i]->checkBoundaries(*innerBoundaries[j]);
803 }
804 }
805
806 float smallest = FLT_MAX, largest = -FLT_MAX;
807 for(unsigned int i=0; i<innerBoundaries.size(); ++i)
808 {
809 innerBoundaries[i]->getSuggestedThicknessRange(smallest, largest);
810 }
811
812 OSG_INFO<<"Smallest = "<<smallest<<std::endl;
813 OSG_INFO<<"Largest = "<<largest<<std::endl;
814
815 float targetThickness = largest*0.75f;
816
817 #if 1
818 for(unsigned int i=0; i<innerBoundaries.size(); ++i)
819 {
820 innerBoundaries[i]->applyThickness(targetThickness);
821 }
822
823 for(Boundaries::iterator itr = innerBoundaries.begin();
824 itr != innerBoundaries.end();
825 ++itr)
826 {
827 (*itr)->removeAllSegmentsBelowThickness(targetThickness*0.75f);
828 }
829
830 #if 1
831 for(unsigned int i=0; i<innerBoundaries.size(); ++i)
832 {
833 for(unsigned int j=0; j<innerBoundaries.size(); ++j)
834 {
835 innerBoundaries[i]->checkBoundaries(*innerBoundaries[j]);
836 }
837 }
838 #endif
839 #endif
840
841 for(Boundaries::iterator itr = innerBoundaries.begin();
842 itr != innerBoundaries.end();
843 ++itr)
844 {
845 (*itr)->applySuggestedThickness();
846 (*itr)->addBoundaryToGeometry(new_geometry.get(), bevelThickness, "face", "bevel");
847 }
848
849 OSG_INFO<<"Handling shell"<<std::endl;
850 for(unsigned int i=0; i<outerBoundaries.size(); ++i)
851 {
852 for(unsigned int j=0; j<outerBoundaries.size(); ++j)
853 {
854 outerBoundaries[i]->checkBoundaries(*outerBoundaries[j]);
855 }
856 }
857
858 for(Boundaries::iterator itr = outerBoundaries.begin();
859 itr != outerBoundaries.end();
860 ++itr)
861 {
862 (*itr)->applySuggestedThickness();
863 (*itr)->addBoundaryToGeometry(new_geometry.get(), -shellThickness, "", "shell");
864 }
865
866
867
868
869 osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(new_geometry->getVertexArray());
870
871 // need to tessellate the inner boundary
872 {
873 osg::ref_ptr<osg::Geometry> face_geometry = new osg::Geometry;
874 face_geometry->setVertexArray(vertices);
875
876 osg::CopyOp copyop(osg::CopyOp::DEEP_COPY_ALL);
877
878 osg::Geometry::PrimitiveSetList primitiveSets;
879
880 for(osg::Geometry::PrimitiveSetList::iterator itr = new_geometry->getPrimitiveSetList().begin();
881 itr != new_geometry->getPrimitiveSetList().end();
882 ++itr)
883 {
884 osg::PrimitiveSet* prim = itr->get();
885 if (prim->getName()=="face") face_geometry->addPrimitiveSet(copyop(itr->get()));
886 else primitiveSets.push_back(prim);
887 }
888
889 osgUtil::Tessellator ts;
890 ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
891 ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
892 ts.retessellatePolygons(*face_geometry);
893
894 osg::TriangleIndexFunctor<CollectTriangleIndicesFunctor> ctif;
895 face_geometry->accept(ctif);
896 CollectTriangleIndicesFunctor::Indices& indices = ctif._indices;
897
898 // remove the previous primitive sets
899 new_geometry->getPrimitiveSetList().clear();
900
901 // create a front face using triangle indices
902 osg::DrawElementsUShort* front_face = new osg::DrawElementsUShort(GL_TRIANGLES);
903 front_face->setName("face");
904 new_geometry->addPrimitiveSet(front_face);
905 for(unsigned int i=0; i<indices.size();++i)
906 {
907 front_face->push_back(indices[i]);
908 }
909
910 for(osg::Geometry::PrimitiveSetList::iterator itr = primitiveSets.begin();
911 itr != primitiveSets.end();
912 ++itr)
913 {
914 osg::PrimitiveSet* prim = itr->get();
915 if (prim->getName()!="face") new_geometry->addPrimitiveSet(prim);
916 }
917 }
918
919 #if REPORT_TIME
920 OSG_NOTICE<<"Time to compute 3d glyp geometry: "<<timer.elapsedTime_m()<<"ms"<<std::endl;
921 #endif
922
923 return new_geometry.release();
924 }
925
926 /////////////////////////////////////////////////////////////////////////////////////////
927 //
928 // computeTextGeometry
929 //
computeTextGeometry(const osgText::Glyph3D * glyph,float width)930 OSGTEXT_EXPORT osg::Geometry* computeTextGeometry(const osgText::Glyph3D* glyph, float width)
931 {
932 const osg::Vec3Array* orig_vertices = glyph->getRawVertexArray();
933 const osg::Geometry::PrimitiveSetList& orig_primitives = glyph->getRawFacePrimitiveSetList();
934
935 osg::ref_ptr<osg::Geometry> text_geometry = new osg::Geometry;
936 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array((*orig_vertices));
937
938 text_geometry->setVertexArray(vertices.get());
939 text_geometry->setPrimitiveSetList(orig_primitives);
940
941 osgUtil::Tessellator ts;
942 ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
943 ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
944 ts.retessellatePolygons(*text_geometry);
945
946 osg::TriangleIndexFunctor<CollectTriangleIndicesFunctor> ctif;
947 text_geometry->accept(ctif);
948 CollectTriangleIndicesFunctor::Indices& indices = ctif._indices;
949
950 // remove the previous primitive sets
951 text_geometry->getPrimitiveSetList().clear();
952
953 if (indices.empty()) return 0;
954
955
956 // create a front face using triangle indices
957 osg::DrawElementsUShort* frontFace = new osg::DrawElementsUShort(GL_TRIANGLES);
958 frontFace->setName("front");
959 text_geometry->addPrimitiveSet(frontFace);
960 for(unsigned int i=0; i<indices.size();++i)
961 {
962 frontFace->push_back(indices[i]);
963 }
964
965 typedef std::vector<unsigned int> Indices;
966 const unsigned int NULL_VALUE = UINT_MAX;
967 Indices back_indices;
968 back_indices.resize(vertices->size(), NULL_VALUE);
969 osg::Vec3 forward(0,0,-width);
970
971 // build up the vertices primitives for the back face, and record the indices
972 // for later use, and to ensure sharing of vertices in the face primitive set
973 // the order of the triangle indices are flipped to make sure that the triangles are back face
974 osg::DrawElementsUShort* backFace = new osg::DrawElementsUShort(GL_TRIANGLES);
975 backFace->setName("back");
976 text_geometry->addPrimitiveSet(backFace);
977 for(unsigned int i=0; i<indices.size()-2;)
978 {
979 unsigned int p1 = indices[i++];
980 unsigned int p2 = indices[i++];
981 unsigned int p3 = indices[i++];
982 if (back_indices[p1]==NULL_VALUE)
983 {
984 back_indices[p1] = vertices->size();
985 vertices->push_back((*vertices)[p1]+forward);
986 }
987
988 if (back_indices[p2]==NULL_VALUE)
989 {
990 back_indices[p2] = vertices->size();
991 vertices->push_back((*vertices)[p2]+forward);
992 }
993
994 if (back_indices[p3]==NULL_VALUE)
995 {
996 back_indices[p3] = vertices->size();
997 vertices->push_back((*vertices)[p3]+forward);
998 }
999
1000 backFace->push_back(back_indices[p1]);
1001 backFace->push_back(back_indices[p3]);
1002 backFace->push_back(back_indices[p2]);
1003 }
1004
1005 unsigned int orig_size = orig_vertices->size();
1006 Indices frontedge_indices, backedge_indices;
1007 frontedge_indices.resize(orig_size, NULL_VALUE);
1008 backedge_indices.resize(orig_size, NULL_VALUE);
1009
1010
1011 for(osg::Geometry::PrimitiveSetList::const_iterator itr = orig_primitives.begin();
1012 itr != orig_primitives.end();
1013 ++itr)
1014 {
1015 osg::DrawElementsUShort* edging = new osg::DrawElementsUShort(osg::PrimitiveSet::QUAD_STRIP);
1016 edging->setName("wall");
1017 text_geometry->addPrimitiveSet(edging);
1018
1019 osg::DrawElementsUShort* elements = dynamic_cast<osg::DrawElementsUShort*>(itr->get());
1020 if (elements)
1021 {
1022 for(unsigned int i=0; i<elements->size(); ++i)
1023 {
1024 unsigned int ei = (*elements)[i];
1025 if (frontedge_indices[ei]==NULL_VALUE)
1026 {
1027 frontedge_indices[ei] = vertices->size();
1028 vertices->push_back((*orig_vertices)[ei]);
1029 }
1030 if (backedge_indices[ei]==NULL_VALUE)
1031 {
1032 backedge_indices[ei] = vertices->size();
1033 vertices->push_back((*orig_vertices)[ei]+forward);
1034 }
1035
1036 edging->push_back(backedge_indices[ei]);
1037 edging->push_back(frontedge_indices[ei]);
1038 }
1039 }
1040 }
1041
1042 return text_geometry.release();
1043 }
1044
1045 /////////////////////////////////////////////////////////////////////////////////////////
1046 //
1047 // computeTextGeometry
1048 //
computeTextGeometry(osg::Geometry * glyphGeometry,const osgText::Bevel & profile,float width)1049 OSGTEXT_EXPORT osg::Geometry* computeTextGeometry(osg::Geometry* glyphGeometry, const osgText::Bevel& profile, float width)
1050 {
1051 if (!glyphGeometry)
1052 {
1053 OSG_NOTICE<<"Warning: computeTextGeometry(..) error, glyphGeometry="<<glyphGeometry<<std::endl;
1054 return 0;
1055 }
1056
1057 osg::Vec3Array* orig_vertices = dynamic_cast<osg::Vec3Array*>(glyphGeometry->getVertexArray());
1058 if (!orig_vertices)
1059 {
1060 OSG_INFO<<"Warning: computeTextGeometry(..): No vertices on glyphGeometry."<<std::endl;
1061 return 0;
1062 }
1063
1064 osg::ref_ptr<osg::Geometry> text_geometry = new osg::Geometry;
1065 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
1066 text_geometry->setVertexArray(vertices.get());
1067
1068 typedef std::vector<unsigned int> Indices;
1069 const unsigned int NULL_VALUE = UINT_MAX;
1070 Indices front_indices, back_indices;
1071 front_indices.resize(orig_vertices->size(), NULL_VALUE);
1072 back_indices.resize(orig_vertices->size(), NULL_VALUE);
1073
1074 osg::DrawElementsUShort* face = 0;
1075 osg::Geometry::PrimitiveSetList bevelPrimitiveSets;
1076 osg::Vec3 forward(0,0,-width);
1077
1078 // collect bevels and face primitive sets
1079 for(osg::Geometry::PrimitiveSetList::iterator itr = glyphGeometry->getPrimitiveSetList().begin();
1080 itr != glyphGeometry->getPrimitiveSetList().end();
1081 ++itr)
1082 {
1083 osg::PrimitiveSet* prim = itr->get();
1084 if (prim->getName()=="face") face = dynamic_cast<osg::DrawElementsUShort*>(prim);
1085 else if (prim->getName()=="bevel") bevelPrimitiveSets.push_back(prim);
1086 }
1087
1088 // if we don't have a face we can't create any 3d text
1089 if (!face) return 0;
1090
1091 // face doesn't have enough vertices on it to represent a polygon.
1092 if (face->size()<3)
1093 {
1094 OSG_NOTICE<<"Face does not have enough elements to be able to represent a polygon, face->size() = "<<face->size()<<std::endl;
1095 return 0;
1096 }
1097
1098 // build up the vertices primitives for the front face, and record the indices
1099 // for later use, and to ensure sharing of vertices in the face primitive set
1100 osg::DrawElementsUShort* frontFace = new osg::DrawElementsUShort(GL_TRIANGLES);
1101 frontFace->setName("front");
1102 text_geometry->addPrimitiveSet(frontFace);
1103 for(unsigned int i=0; i<face->size();)
1104 {
1105 unsigned int pi = (*face)[i++];
1106 if (front_indices[pi]==NULL_VALUE)
1107 {
1108 front_indices[pi] = vertices->size();
1109 vertices->push_back((*orig_vertices)[pi]);
1110 }
1111 frontFace->push_back(front_indices[pi]);
1112 }
1113
1114
1115 // build up the vertices primitives for the back face, and record the indices
1116 // for later use, and to ensure sharing of vertices in the face primitive set
1117 // the order of the triangle indices are flipped to make sure that the triangles are back face
1118 osg::DrawElementsUShort* backFace = new osg::DrawElementsUShort(GL_TRIANGLES);
1119 backFace->setName("back");
1120 text_geometry->addPrimitiveSet(backFace);
1121 for(unsigned int i=0; i<face->size()-2;)
1122 {
1123 unsigned int p1 = (*face)[i++];
1124 unsigned int p2 = (*face)[i++];
1125 unsigned int p3 = (*face)[i++];
1126 if (back_indices[p1]==NULL_VALUE)
1127 {
1128 back_indices[p1] = vertices->size();
1129 vertices->push_back((*orig_vertices)[p1]+forward);
1130 }
1131
1132 if (back_indices[p2]==NULL_VALUE)
1133 {
1134 back_indices[p2] = vertices->size();
1135 vertices->push_back((*orig_vertices)[p2]+forward);
1136 }
1137
1138 if (back_indices[p3]==NULL_VALUE)
1139 {
1140 back_indices[p3] = vertices->size();
1141 vertices->push_back((*orig_vertices)[p3]+forward);
1142 }
1143
1144 backFace->push_back(back_indices[p1]);
1145 backFace->push_back(back_indices[p3]);
1146 backFace->push_back(back_indices[p2]);
1147 }
1148
1149 // now build up the bevel
1150 for(osg::Geometry::PrimitiveSetList::iterator itr = bevelPrimitiveSets.begin();
1151 itr != bevelPrimitiveSets.end();
1152 ++itr)
1153 {
1154 osg::DrawElementsUShort* bevel = dynamic_cast<osg::DrawElementsUShort*>(itr->get());
1155 if (!bevel) continue;
1156
1157 unsigned int no_vertices_on_boundary = bevel->size()/2;
1158
1159 const osgText::Bevel::Vertices& profileVertices = profile.getVertices();
1160 unsigned int no_vertices_on_bevel = profileVertices.size();
1161
1162 Indices bevelIndices;
1163 bevelIndices.resize(no_vertices_on_boundary*no_vertices_on_bevel, NULL_VALUE);
1164
1165 // populate vertices
1166 for(unsigned int i=0; i<no_vertices_on_boundary; ++i)
1167 {
1168 unsigned int topi = (*bevel)[i*2];
1169 unsigned int basei = (*bevel)[i*2+1];
1170
1171 osg::Vec3& top_vertex = (*orig_vertices)[ topi ];
1172 osg::Vec3& base_vertex = (*orig_vertices)[ basei ];
1173 osg::Vec3 up = top_vertex-base_vertex;
1174
1175 if (front_indices[basei]==NULL_VALUE)
1176 {
1177 front_indices[basei] = vertices->size();
1178 vertices->push_back(base_vertex);
1179 }
1180
1181 bevelIndices[i*no_vertices_on_bevel + 0] = front_indices[basei];
1182
1183 for(unsigned int j=1; j<no_vertices_on_bevel-1; ++j)
1184 {
1185 const osg::Vec2& pv = profileVertices[j];
1186 osg::Vec3 pos( base_vertex + (forward * pv.x()) + (up * pv.y()) );
1187 bevelIndices[i*no_vertices_on_bevel + j] = vertices->size();
1188 vertices->push_back(pos);
1189 }
1190
1191 if (back_indices[basei]==NULL_VALUE)
1192 {
1193 back_indices[basei] = vertices->size();
1194 vertices->push_back(base_vertex + forward);
1195 }
1196
1197 bevelIndices[i*no_vertices_on_bevel + no_vertices_on_bevel-1] = back_indices[basei];
1198 }
1199
1200 osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(GL_TRIANGLES);
1201 elements->setName("wall");
1202 unsigned int base, next;
1203 for(unsigned int i = 0; i< no_vertices_on_boundary-1; ++i)
1204 {
1205 for(unsigned int j=0; j<no_vertices_on_bevel-1; ++j)
1206 {
1207 base = i*no_vertices_on_bevel + j;
1208 next = base + no_vertices_on_bevel;
1209
1210 elements->push_back(bevelIndices[base]);
1211 elements->push_back(bevelIndices[next]);
1212 elements->push_back(bevelIndices[base+1]);
1213
1214 elements->push_back(bevelIndices[base+1]);
1215 elements->push_back(bevelIndices[next]);
1216 elements->push_back(bevelIndices[next+1]);
1217 }
1218 }
1219
1220 text_geometry->addPrimitiveSet(elements);
1221 }
1222
1223 return text_geometry.release();
1224 }
1225
1226 /////////////////////////////////////////////////////////////////////////////////////////
1227 //
1228 // computeShellGeometry
1229 //
computeShellGeometry(osg::Geometry * glyphGeometry,const osgText::Bevel & profile,float width)1230 OSGTEXT_EXPORT osg::Geometry* computeShellGeometry(osg::Geometry* glyphGeometry, const osgText::Bevel& profile, float width)
1231 {
1232 if (!glyphGeometry)
1233 {
1234 OSG_NOTICE<<"Warning: computeShellGeometry(..) error, glyphGeometry="<<glyphGeometry<<std::endl;
1235 return 0;
1236 }
1237
1238 osg::Vec3Array* orig_vertices = dynamic_cast<osg::Vec3Array*>(glyphGeometry->getVertexArray());
1239 if (!orig_vertices)
1240 {
1241 OSG_NOTICE<<"computeTextGeometry(..): No vertices on glyphGeometry."<<std::endl;
1242 return 0;
1243 }
1244
1245 osg::ref_ptr<osg::Geometry> text_geometry = new osg::Geometry;
1246 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
1247 text_geometry->setVertexArray(vertices.get());
1248
1249 typedef std::vector<unsigned int> Indices;
1250 const unsigned int NULL_VALUE = UINT_MAX;
1251 Indices front_indices, back_indices;
1252 front_indices.resize(orig_vertices->size(), NULL_VALUE);
1253 back_indices.resize(orig_vertices->size(), NULL_VALUE);
1254
1255 osg::DrawElementsUShort* face = 0;
1256 osg::Geometry::PrimitiveSetList bevelPrimitiveSets;
1257 osg::Geometry::PrimitiveSetList shellPrimitiveSets;
1258 osg::Vec3 frontOffset(0,0,width);
1259 osg::Vec3 backOffset(0,0,-2.0*width);
1260 osg::Vec3 forward(backOffset-frontOffset);
1261
1262 // collect bevels and face primitive sets
1263 for(osg::Geometry::PrimitiveSetList::iterator itr = glyphGeometry->getPrimitiveSetList().begin();
1264 itr != glyphGeometry->getPrimitiveSetList().end();
1265 ++itr)
1266 {
1267 osg::PrimitiveSet* prim = itr->get();
1268 if (prim->getName()=="face") face = dynamic_cast<osg::DrawElementsUShort*>(prim);
1269 else if (prim->getName()=="bevel") bevelPrimitiveSets.push_back(prim);
1270 else if (prim->getName()=="shell") shellPrimitiveSets.push_back(prim);
1271 }
1272
1273 // if we don't have a face we can't create any 3d text
1274 if (!face) return 0;
1275
1276 // build up the vertices primitives for the front face, and record the indices
1277 // for later use, and to ensure sharing of vertices in the face primitive set
1278 // the order of the triangle indices are flipped to make sure that the triangles are back face
1279 osg::DrawElementsUShort* frontFace = new osg::DrawElementsUShort(GL_TRIANGLES);
1280 text_geometry->addPrimitiveSet(frontFace);
1281 for(unsigned int i=0; i<face->size()-2;)
1282 {
1283 unsigned int p1 = (*face)[i++];
1284 unsigned int p2 = (*face)[i++];
1285 unsigned int p3 = (*face)[i++];
1286 if (front_indices[p1]==NULL_VALUE)
1287 {
1288 front_indices[p1] = vertices->size();
1289 vertices->push_back((*orig_vertices)[p1]+frontOffset);
1290 }
1291
1292 if (front_indices[p2]==NULL_VALUE)
1293 {
1294 front_indices[p2] = vertices->size();
1295 vertices->push_back((*orig_vertices)[p2]+frontOffset);
1296 }
1297
1298 if (front_indices[p3]==NULL_VALUE)
1299 {
1300 front_indices[p3] = vertices->size();
1301 vertices->push_back((*orig_vertices)[p3]+frontOffset);
1302 }
1303
1304 frontFace->push_back(front_indices[p1]);
1305 frontFace->push_back(front_indices[p3]);
1306 frontFace->push_back(front_indices[p2]);
1307 }
1308
1309
1310 // build up the vertices primitives for the back face, and record the indices
1311 // for later use, and to ensure sharing of vertices in the face primitive set
1312 osg::DrawElementsUShort* backFace = new osg::DrawElementsUShort(GL_TRIANGLES);
1313 text_geometry->addPrimitiveSet(backFace);
1314 for(unsigned int i=0; i<face->size();)
1315 {
1316 unsigned int pi = (*face)[i++];
1317 if (back_indices[pi]==NULL_VALUE)
1318 {
1319 back_indices[pi] = vertices->size();
1320 vertices->push_back((*orig_vertices)[pi]+backOffset);
1321 }
1322 backFace->push_back(back_indices[pi]);
1323 }
1324
1325 for(osg::Geometry::PrimitiveSetList::iterator itr = bevelPrimitiveSets.begin();
1326 itr != bevelPrimitiveSets.end();
1327 ++itr)
1328 {
1329 osg::DrawElementsUShort* strip = dynamic_cast<osg::DrawElementsUShort*>(itr->get());
1330 if (!strip) continue;
1331
1332 osg::CopyOp copyop(osg::CopyOp::DEEP_COPY_ALL);
1333
1334 osg::DrawElementsUShort* front_strip = new osg::DrawElementsUShort(*strip, copyop);
1335 text_geometry->addPrimitiveSet(front_strip);
1336 for(unsigned int i=0; i<front_strip->size(); ++i)
1337 {
1338 unsigned short& pi = (*front_strip)[i];
1339 if (front_indices[pi]==NULL_VALUE)
1340 {
1341 front_indices[pi] = vertices->size();
1342 vertices->push_back((*orig_vertices)[pi]+frontOffset);
1343 }
1344 pi = front_indices[pi];
1345 }
1346
1347 for(unsigned int i=0; i<front_strip->size()-1;)
1348 {
1349 unsigned short& p1 = (*front_strip)[i++];
1350 unsigned short& p2 = (*front_strip)[i++];
1351 std::swap(p1,p2);
1352 }
1353
1354 osg::DrawElementsUShort* back_strip = new osg::DrawElementsUShort(*strip, copyop);
1355 text_geometry->addPrimitiveSet(back_strip);
1356 for(unsigned int i=0; i<back_strip->size(); ++i)
1357 {
1358 unsigned short& pi = (*back_strip)[i];
1359 if (back_indices[pi]==NULL_VALUE)
1360 {
1361 back_indices[pi] = vertices->size();
1362 vertices->push_back((*orig_vertices)[pi]+backOffset);
1363 }
1364 pi = back_indices[pi];
1365 }
1366 }
1367
1368
1369 // now build up the shell
1370 for(osg::Geometry::PrimitiveSetList::iterator itr = shellPrimitiveSets.begin();
1371 itr != shellPrimitiveSets.end();
1372 ++itr)
1373 {
1374 osg::DrawElementsUShort* bevel = dynamic_cast<osg::DrawElementsUShort*>(itr->get());
1375 if (!bevel) continue;
1376
1377 unsigned int no_vertices_on_boundary = bevel->size()/2;
1378
1379 const osgText::Bevel::Vertices& profileVertices = profile.getVertices();
1380 unsigned int no_vertices_on_bevel = profileVertices.size();
1381
1382 Indices bevelIndices;
1383 bevelIndices.resize(no_vertices_on_boundary*no_vertices_on_bevel, NULL_VALUE);
1384
1385 // populate vertices
1386 for(unsigned int i=0; i<no_vertices_on_boundary; ++i)
1387 {
1388 unsigned int topi = (*bevel)[i*2+1];
1389 unsigned int basei = (*bevel)[i*2];
1390
1391 osg::Vec3 top_vertex = (*orig_vertices)[ topi ] + frontOffset;
1392 osg::Vec3 base_vertex = (*orig_vertices)[ basei ] + frontOffset;
1393 osg::Vec3 up = top_vertex-base_vertex;
1394
1395 if (front_indices[basei]==NULL_VALUE)
1396 {
1397 front_indices[basei] = vertices->size();
1398 vertices->push_back(base_vertex);
1399 }
1400
1401 bevelIndices[i*no_vertices_on_bevel + 0] = front_indices[basei];
1402
1403 for(unsigned int j=1; j<no_vertices_on_bevel-1; ++j)
1404 {
1405 const osg::Vec2& pv = profileVertices[j];
1406 osg::Vec3 pos( base_vertex + (forward * pv.x()) + (up * pv.y()) );
1407 bevelIndices[i*no_vertices_on_bevel + j] = vertices->size();
1408 vertices->push_back(pos);
1409 }
1410
1411 if (back_indices[basei]==NULL_VALUE)
1412 {
1413 back_indices[basei] = vertices->size();
1414 vertices->push_back(base_vertex + forward);
1415 }
1416
1417 bevelIndices[i*no_vertices_on_bevel + no_vertices_on_bevel-1] = back_indices[basei];
1418 }
1419
1420 osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(GL_TRIANGLES);
1421 unsigned int base, next;
1422 for(unsigned int i = 0; i< no_vertices_on_boundary-1; ++i)
1423 {
1424 for(unsigned int j=0; j<no_vertices_on_bevel-1; ++j)
1425 {
1426 base = i*no_vertices_on_bevel + j;
1427 next = base + no_vertices_on_bevel;
1428
1429 elements->push_back(bevelIndices[base]);
1430 elements->push_back(bevelIndices[base+1]);
1431 elements->push_back(bevelIndices[next]);
1432
1433 elements->push_back(bevelIndices[base+1]);
1434 elements->push_back(bevelIndices[next+1]);
1435 elements->push_back(bevelIndices[next]);
1436 }
1437 }
1438
1439 text_geometry->addPrimitiveSet(elements);
1440 }
1441
1442 #if 1
1443 osg::Vec4Array* new_colours = new osg::Vec4Array;
1444 new_colours->push_back(osg::Vec4(1.0,1.0,1.0,0.2));
1445 text_geometry->setColorArray(new_colours, osg::Array::BIND_OVERALL);
1446
1447
1448 osg::StateSet* stateset = text_geometry->getOrCreateStateSet();
1449 stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
1450 stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
1451 stateset->setAttributeAndModes(new osg::CullFace, osg::StateAttribute::ON);
1452 //stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1453 stateset->setRenderBinDetails(11, "SORT_FRONT_TO_BACK");
1454 #endif
1455 return text_geometry.release();
1456 }
1457
1458 }
1459