From 820166b49de9cdebbb3641a164b48fd8820d0038 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 26 May 2015 10:05:47 +0000 Subject: [PATCH] Rewrote the Text3D bevel implementation to automatically adjust bevel thickness to avoid overalapping and erronous tesselation. Added osgText::Bevel::s/getRoundedConcaveJunctions(bool) to control how the bevel should be tessellated around concave junctions on the glyph boundary. git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14871 16af8721-9629-0410-8352-f15c8da7e697 --- examples/osgtext3D/TextNode.cpp | 4 +- examples/osgtext3D/osgtext3D.cpp | 10 + include/osgText/Style | 5 + src/osgText/Glyph.cpp | 4 +- src/osgText/GlyphGeometry.cpp | 501 ++++++++++++++++++++++++++++--- src/osgText/GlyphGeometry.h | 2 +- src/osgText/Style.cpp | 2 + 7 files changed, 481 insertions(+), 47 deletions(-) diff --git a/examples/osgtext3D/TextNode.cpp b/examples/osgtext3D/TextNode.cpp index 8a19af9f5..5d5b32c31 100644 --- a/examples/osgtext3D/TextNode.cpp +++ b/examples/osgtext3D/TextNode.cpp @@ -183,9 +183,7 @@ void TextTechnique::addCharacter(const osg::Vec3& position, const osg::Vec3& siz if (bevel) { - float thickness = bevel->getBevelThickness(); - - osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, thickness, width); + osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, *bevel, width); osg::ref_ptr textGeometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width); osg::ref_ptr shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0; if (textGeometry.valid()) geode->addDrawable(textGeometry.get()); diff --git a/examples/osgtext3D/osgtext3D.cpp b/examples/osgtext3D/osgtext3D.cpp index 903df2253..8641800f6 100644 --- a/examples/osgtext3D/osgtext3D.cpp +++ b/examples/osgtext3D/osgtext3D.cpp @@ -62,7 +62,17 @@ int main(int argc, char** argv) while(arguments.read("--flat",r)) { bevel = new osgText::Bevel; bevel->flatBevel(r); } while(arguments.read("--flat")) { bevel = new osgText::Bevel; bevel->flatBevel(0.25); } while(arguments.read("--bevel-thickness",r)) { if (bevel.valid()) bevel->setBevelThickness(r); } + + + if (bevel.valid()) + { + while(arguments.read("--smooth-concave-Junctions") || arguments.read("--scj")) + { + bevel->setSmoothConcaveJunctions(true); + } + } + style->setBevel(bevel.get()); // set up outline. diff --git a/include/osgText/Style b/include/osgText/Style index 397879b08..b2ca5a3e2 100644 --- a/include/osgText/Style +++ b/include/osgText/Style @@ -35,10 +35,14 @@ class OSGTEXT_EXPORT Bevel : public osg::Object bool operator == (const Bevel& rhs) const { + if (_smoothConcaveJunctions != rhs._smoothConcaveJunctions) return false; if (_thickness != rhs._thickness) return false; return _vertices==rhs._vertices; } + void setSmoothConcaveJunctions(bool flag) { _smoothConcaveJunctions = flag; } + bool getSmoothConcaveJunctions() const { return _smoothConcaveJunctions; } + void setBevelThickness(float thickness) { _thickness = thickness; } float getBevelThickness() const { return _thickness; } @@ -58,6 +62,7 @@ class OSGTEXT_EXPORT Bevel : public osg::Object protected: + bool _smoothConcaveJunctions; float _thickness; Vertices _vertices; }; diff --git a/src/osgText/Glyph.cpp b/src/osgText/Glyph.cpp index 3f98eb23a..325dbc89b 100644 --- a/src/osgText/Glyph.cpp +++ b/src/osgText/Glyph.cpp @@ -626,9 +626,7 @@ void GlyphGeometry::setup(const Glyph3D* glyph, const Style* style) if (bevel) { - float thickness = bevel->getBevelThickness(); - - osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, thickness, width); + osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, *bevel, width); _geometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width); shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0; diff --git a/src/osgText/GlyphGeometry.cpp b/src/osgText/GlyphGeometry.cpp index 9b369c80a..7a30525ea 100644 --- a/src/osgText/GlyphGeometry.cpp +++ b/src/osgText/GlyphGeometry.cpp @@ -18,9 +18,17 @@ #include #include #include +#include #include + +#define REPORT_TIME 0 + +#if REPORT_TIME +#include +#endif + namespace osgText { @@ -35,24 +43,27 @@ public: struct Segment { Segment(unsigned int f, unsigned int s, float t): - first(f), second(s), thickness(t) {} + first(f), second(s), thickness(t), suggestedThickness(t) {} Segment(const Segment& seg): first(seg.first), second(seg.second), - thickness(seg.thickness) {} + thickness(seg.thickness), + suggestedThickness(seg.suggestedThickness) {} Segment& operator = (const Segment& seg) { first = seg.first; second = seg.second; thickness = seg.thickness; + suggestedThickness = seg.suggestedThickness; return *this; } unsigned int first; unsigned int second; float thickness; + float suggestedThickness; }; @@ -61,8 +72,10 @@ public: osg::ref_ptr _vertices; osg::ref_ptr _elements; Segments _segments; + bool verbose; - Boundary(const osg::Vec3Array* vertices, const osg::PrimitiveSet* primitiveSet, float thickness) + Boundary(const osg::Vec3Array* vertices, const osg::PrimitiveSet* primitiveSet, float thickness): + verbose(false) { const osg::DrawArrays* drawArrays = dynamic_cast(primitiveSet); if (drawArrays) @@ -102,6 +115,52 @@ public: _segments.push_back( Segment((*elements)[i], (*elements)[i+1], thickness) ); } } + + bool shorter(float original_thickness, float new_thickness) const { return (original_thickness<0.0f) ? (new_thickness>original_thickness) : (new_thicknesslargest) largest = t; + } + + if (largest<0.0f) std::swap(smallest, largest); + } osg::Vec3 computeRayIntersectionPoint(const osg::Vec3& a, const osg::Vec3& an, const osg::Vec3& c, const osg::Vec3& cn) { @@ -252,7 +311,7 @@ public: } } - osg::Vec3 computeBisectorPoint(unsigned int i, float targetThickness) + float computeBisectorPoint(unsigned int i, float targetThickness, osg::Vec3& va, osg::Vec3& vb) { Segment& seg_before = _segments[ (i+_segments.size()-1) % _segments.size() ]; Segment& seg_target = _segments[ (i) % _segments.size() ]; @@ -265,15 +324,166 @@ public: osg::Vec3 ab_sidevector(b.y()-a.y(), a.x()-b.x(), 0.0); ab_sidevector.normalize(); float scale_factor = 1.0/ (bisector_abcd*ab_sidevector); - osg::Vec3 new_vertex = intersection_abcd + bisector_abcd*(scale_factor*targetThickness); + float new_thickness = scale_factor*targetThickness; + osg::Vec3 new_vertex = intersection_abcd + bisector_abcd*new_thickness; - // OSG_NOTICE<<"bisector_abcd = "< orig_vertices = new osg::Vec3Array; + osg::Geometry::PrimitiveSetList orig_primitives; + + orig_vertices->reserve(source_vertices->size()); + orig_primitives.reserve(source_primitives.size()); + + typedef std::vector Indices; + Indices remappedIndices(source_vertices->size(), -1); + + float convexCornerInsertionWidth = 0.02; + float convexCornerCutoffAngle = osg::inDegrees(45.0f); + + int num_vertices_on_web = 10; + + + for(osg::Geometry::PrimitiveSetList::const_iterator itr = source_primitives.begin(); + itr != source_primitives.end(); + ++itr) + { + const osg::DrawElementsUShort* elements = dynamic_cast(itr->get()); + if (elements && elements->size()>2) + { + osg::ref_ptr new_elements = new osg::DrawElementsUShort(elements->getMode()); + orig_primitives.push_back(new_elements.get()); + + int num_indices = elements->size(); + for(int i = 0; isize()-2)] : (*elements)[i-1]; + int vi_curr = (*elements)[i]; + int vi_after = (*elements)[i+1]; + + osg::Vec3 va = (*source_vertices)[vi_before]; + osg::Vec3 vb = (*source_vertices)[vi_curr]; + osg::Vec3 vc = (*source_vertices)[vi_after]; + + // OSG_NOTICE<<" "<=static_cast(remappedIndices.size())) remappedIndices.resize(vi_curr+1,-1); + + int new_index = remappedIndices[vi_curr]; + if (new_index<0) + { + remappedIndices[vi_curr] = new_index = orig_vertices->size(); + orig_vertices->push_back(vb); + } + new_elements->push_back(new_index); + + if (roundedWebs) + { + osg::Vec3 vab = vb-va; + osg::Vec3 vbc = vc-vb; + float len_vab = vab.normalize(); + float len_vbc = vbc.normalize(); + + if (len_vab*len_vbc==0.0f) + { + OSG_NOTICE<<"Warning: len_vab="<convexCornerCutoffAngle) + { + vab.normalize(); + vbc.normalize(); + + float min_len = osg::minimum(len_vab, len_vbc); + + osg::Vec3 v_before = vb - vab*(min_len*convexCornerInsertionWidth); + osg::Vec3 v_after = vb + vbc*(min_len*convexCornerInsertionWidth); + osg::Vec3 v_mid = v_before + (v_after-v_before)*0.5f; + + float h = (vb-v_mid).length(); + float w = (v_after-v_before).length()*0.5f; + float l = w*w / h; + float r = sqrt(l*l + w*w); + float alpha = atan2(w,h); + float beta = osg::PI-alpha*2.0f; + + // OSG_NOTICE<<" h = "<checkBoundaries(*innerBoundaries[j]); + } + } + + float smallest = FLT_MAX, largest = -FLT_MAX; + for(unsigned int i=0; igetSuggestedThicknessRange(smallest, largest); + } + + OSG_INFO<<"Smallest = "<checkBoundaries(*outerBoundaries[j]); + } + } + + for(Boundaries::iterator itr = outerBoundaries.begin(); + itr != outerBoundaries.end(); ++itr) { + (*itr)->applySuggestedThickness(); (*itr)->addBoundaryToGeometry(new_geometry.get(), -shellThickness, "", "shell"); } + + -#else - for(osg::Geometry::PrimitiveSetList::const_iterator itr = orig_primitives.begin(); - itr != orig_primitives.end(); - ++itr) - { - if ((*itr)->getMode()==GL_POLYGON) - { - Boundary boundaryInner(orig_vertices, itr->get()); - boundaryInner.removeAllSegmentsBelowThickness(bevelThickness); - boundaryInner.addBoundaryToGeometry(new_geometry.get(), bevelThickness, "face", "bevel"); - - Boundary boundaryOuter(orig_vertices, itr->get()); - boundaryOuter.removeAllSegmentsAboveThickness(-shellThickness); - boundaryOuter.addBoundaryToGeometry(new_geometry.get(), -shellThickness, "", "shell"); - } - - } -#endif osg::Vec3Array* vertices = dynamic_cast(new_geometry->getVertexArray()); @@ -506,7 +911,11 @@ OSGTEXT_EXPORT osg::Geometry* computeGlyphGeometry(const osgText::Glyph3D* glyph if (prim->getName()!="face") new_geometry->addPrimitiveSet(prim); } } - + +#if REPORT_TIME + OSG_NOTICE<<"Time to compute 3d glyp geometry: "<