diff --git a/examples/osgtext3D/CMakeLists.txt b/examples/osgtext3D/CMakeLists.txt index 79c30c3d3..8c68b00c7 100644 --- a/examples/osgtext3D/CMakeLists.txt +++ b/examples/osgtext3D/CMakeLists.txt @@ -1,6 +1,11 @@ #this file is automatically generated +SET(TARGET_H + GlyphGeometry.h +) + SET(TARGET_SRC + GlyphGeometry.cpp osgtext3D_orig.cpp osgtext3D_test.cpp osgtext3D.cpp diff --git a/examples/osgtext3D/GlyphGeometry.cpp b/examples/osgtext3D/GlyphGeometry.cpp new file mode 100644 index 000000000..4144c291a --- /dev/null +++ b/examples/osgtext3D/GlyphGeometry.cpp @@ -0,0 +1,788 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include "GlyphGeometry.h" + +#include +#include +#include +#include + +#include + +namespace osgText +{ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// Boundary +// +class Boundary +{ +public: + + typedef std::pair Segment; + typedef std::vector Segments; + osg::ref_ptr _vertices; + unsigned int _start; + unsigned int _count; + Segments _segments; + + Boundary(osg::Vec3Array* vertices, unsigned int start, unsigned int count) + { + _vertices = vertices; + _start = start; + _count = count; + + if ((*_vertices)[start]==(*_vertices)[start+count-1]) + { + // OSG_NOTICE<<"Boundary is a line loop"< 0.0f) + { + // OSG_NOTICE<<" computeBisectorNormal(a=["< maxThickness) + { + maxThickness = thickness; + maxThickness_i = i; + } + } + + return maxThickness_i != _segments.size(); + } + + + void removeAllSegmentsAboveThickness(float targetThickness) + { + // OSG_NOTICE<<"removeAllSegmentsBelowThickness("<reserve(new_vertices->size() + _segments.size()+1 + _count); + + // create vertices + unsigned int previous_second = _segments[0].second; + osg::Vec3 newPoint = computeBisectorPoint(0, targetThickness); + unsigned int first = new_vertices->size(); + new_vertices->push_back(newPoint); + + if (_segments[0].first != _start) + { + //OSG_NOTICE<<"We have pruned from the start"<push_back(first); + } + } + else + { + face->push_back(first); + } + + + for(unsigned int i=1; i<_segments.size(); ++i) + { + newPoint = computeBisectorPoint(i, targetThickness); + unsigned int vi = new_vertices->size(); + new_vertices->push_back(newPoint); + + if (previous_second != _segments[i].first) + { + //OSG_NOTICE<<"Gap in boundary"<push_back(vi); + } + } + else + { + face->push_back(vi); + } + + previous_second = _segments[i].second; + } + + // fill the end of the polygon with repititions of the first index in the polygon to ensure + // that the orignal and new boundary polygons have the same number and pairing of indices. + // This ensures that the bevel can be created coherently. + while(face->size() < _count) + { + face->push_back(first); + } + + + if (requireFace) + { + // add face primitive set for polygon + geometry->addPrimitiveSet(face.get()); + } + + + osg::DrawElementsUShort* bevel = new osg::DrawElementsUShort(GL_QUAD_STRIP); + bevel->setName("bevel"); + bevel->reserve(_count*2); + for(unsigned int i=0; i<_count; ++i) + { + unsigned int vi = new_vertices->size(); + new_vertices->push_back((*_vertices)[_start+i]); + bevel->push_back(vi); + bevel->push_back((*face)[i]); + } + geometry->addPrimitiveSet(bevel); + } + + void newAddBoundaryToGeometry(osg::Geometry* geometry, float targetThickness, const std::string& faceName, const std::string& bevelName) + { + if (_segments.empty()) return; + + if (geometry->getVertexArray()==0) geometry->setVertexArray(new osg::Vec3Array(*_vertices)); + osg::Vec3Array* new_vertices = dynamic_cast(geometry->getVertexArray()); + + // allocate the primitive set to store the face geometry + osg::ref_ptr face = new osg::DrawElementsUShort(GL_POLYGON); + face->setName(faceName); + + // reserve enough space in the vertex array to accomodate the vertices associated with the segments + new_vertices->reserve(new_vertices->size() + _segments.size()+1 + _count); + + // create vertices + unsigned int previous_second = _segments[0].second; + osg::Vec3 newPoint = computeBisectorPoint(0, targetThickness); + unsigned int first = new_vertices->size(); + new_vertices->push_back(newPoint); + + if (_segments[0].first != _start) + { + //OSG_NOTICE<<"We have pruned from the start"<push_back(first); + } + } + else + { + face->push_back(first); + } + + + for(unsigned int i=1; i<_segments.size(); ++i) + { + newPoint = computeBisectorPoint(i, targetThickness); + unsigned int vi = new_vertices->size(); + new_vertices->push_back(newPoint); + + if (previous_second != _segments[i].first) + { + //OSG_NOTICE<<"Gap in boundary"<push_back(vi); + } + } + else + { + face->push_back(vi); + } + + previous_second = _segments[i].second; + } + + // fill the end of the polygon with repititions of the first index in the polygon to ensure + // that the orignal and new boundary polygons have the same number and pairing of indices. + // This ensures that the bevel can be created coherently. + while(face->size() < _count) + { + face->push_back(first); + } + + if (!faceName.empty()) + { + // add face primitive set for polygon + geometry->addPrimitiveSet(face.get()); + } + + osg::DrawElementsUShort* bevel = new osg::DrawElementsUShort(GL_QUAD_STRIP); + bevel->setName(bevelName); + bevel->reserve(_count*2); + for(unsigned int i=0; i<_count; ++i) + { + bevel->push_back(_start+i); + bevel->push_back((*face)[i]); + } + geometry->addPrimitiveSet(bevel); + } +}; + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// BevelProfile +// +BevelProfile::BevelProfile() +{ + flatBevel(); +} + +void BevelProfile::flatBevel(float width) +{ + _vertices.clear(); + + if (width>0.5f) width = 0.5f; + + _vertices.push_back(osg::Vec2(0.0f,0.0f)); + + _vertices.push_back(osg::Vec2(width,1.0f)); + + if (width<0.5f) _vertices.push_back(osg::Vec2(1-width,1.0f)); + + _vertices.push_back(osg::Vec2(1.0f,0.0f)); +} + +void BevelProfile::roundedBevel(float width, unsigned int numSteps) +{ + _vertices.clear(); + + if (width>0.5f) width = 0.5f; + + unsigned int i = 0; + for(; i<=numSteps; ++i) + { + float angle = float(osg::PI)*0.5f*(float(i)/float(numSteps)); + _vertices.push_back( osg::Vec2((1.0f-cosf(angle))*width, sinf(angle)) ); + } + + // start the second half one into the curve if the width is half way across + i = width<0.5f ? 0 : 1; + for(; i<=numSteps; ++i) + { + float angle = float(osg::PI)*0.5f*(float(numSteps-i)/float(numSteps)); + _vertices.push_back( osg::Vec2(1.0-(1.0f-cosf(angle))*width, sin(angle)) ); + } +} + +void BevelProfile::roundedBevel2(float width, unsigned int numSteps) +{ + _vertices.clear(); + + if (width>0.5f) width = 0.5f; + + float h = 0.1f; + float r = 1.0f-h; + + _vertices.push_back(osg::Vec2(0.0,0.0)); + + unsigned int i = 0; + for(; i<=numSteps; ++i) + { + float angle = float(osg::PI)*0.5f*(float(i)/float(numSteps)); + _vertices.push_back( osg::Vec2((1.0f-cosf(angle))*width, h + sinf(angle)*r) ); + } + + // start the second half one into the curve if the width is half way across + i = width<0.5f ? 0 : 1; + for(; i<=numSteps; ++i) + { + float angle = float(osg::PI)*0.5f*(float(numSteps-i)/float(numSteps)); + _vertices.push_back( osg::Vec2(1.0-(1.0f-cosf(angle))*width, h + sin(angle)*r) ); + } + + _vertices.push_back(osg::Vec2(1.0,0.0)); + +} + +void BevelProfile::print(std::ostream& fout) +{ + OSG_NOTICE<<"print bevel"< Indices; + Indices _indices; + + void operator() (unsigned int p1, unsigned int p2, unsigned int p3) + { + if (p1==p2 || p2==p3 || p1==p3) + { + return; + } + + _indices.push_back(p1); + _indices.push_back(p3); + _indices.push_back(p2); + + } +}; + + +osg::Geometry* computeGlyphGeometry(osgText::Font3D::Glyph3D* glyph, float bevelThickness, float shellThickness) +{ + osg::Vec3Array* orig_vertices = glyph->getRawVertexArray(); + osg::Geometry::PrimitiveSetList& orig_primitives = glyph->getRawFacePrimitiveSetList(); + + osg::ref_ptr new_geometry = new osg::Geometry; + + for(osg::Geometry::PrimitiveSetList::iterator itr = orig_primitives.begin(); + itr != orig_primitives.end(); + ++itr) + { + osg::DrawArrays* drawArray = dynamic_cast(itr->get()); + if (drawArray && drawArray->getMode()==GL_POLYGON) + { + Boundary boundaryInner(orig_vertices, drawArray->getFirst(), drawArray->getCount()); + boundaryInner.removeAllSegmentsBelowThickness(bevelThickness); + boundaryInner.newAddBoundaryToGeometry(new_geometry, bevelThickness, "face", "bevel"); + + Boundary boundaryOuter(orig_vertices, drawArray->getFirst(), drawArray->getCount()); + boundaryOuter.removeAllSegmentsAboveThickness(-shellThickness); + boundaryOuter.newAddBoundaryToGeometry(new_geometry, -shellThickness, "", "shell"); + } + } + + osg::Vec3Array* vertices = dynamic_cast(new_geometry->getVertexArray()); + + // need to tessellate the inner boundary + { + osg::Geometry* face_geometry = new osg::Geometry; + face_geometry->setVertexArray(vertices); + + osg::CopyOp copyop(osg::CopyOp::DEEP_COPY_ALL); + + osg::Geometry::PrimitiveSetList primitiveSets; + + for(osg::Geometry::PrimitiveSetList::iterator itr = new_geometry->getPrimitiveSetList().begin(); + itr != new_geometry->getPrimitiveSetList().end(); + ++itr) + { + osg::PrimitiveSet* prim = itr->get(); + if (prim->getName()=="face") face_geometry->addPrimitiveSet(copyop(*itr)); + else primitiveSets.push_back(prim); + } + + osgUtil::Tessellator ts; + ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE); + ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); + ts.retessellatePolygons(*face_geometry); + + osg::TriangleIndexFunctor ctif; + face_geometry->accept(ctif); + CollectTriangleIndicesFunctor::Indices& indices = ctif._indices; + + // remove the previous primitive sets + new_geometry->getPrimitiveSetList().clear(); + + // create a front face using triangle indices + osg::DrawElementsUShort* front_face = new osg::DrawElementsUShort(GL_TRIANGLES); + front_face->setName("face"); + new_geometry->addPrimitiveSet(front_face); + for(unsigned int i=0; ipush_back(indices[i]); + } + + for(osg::Geometry::PrimitiveSetList::iterator itr = primitiveSets.begin(); + itr != primitiveSets.end(); + ++itr) + { + osg::PrimitiveSet* prim = itr->get(); + if (prim->getName()!="face") new_geometry->addPrimitiveSet(prim); + } + } + + return new_geometry.release(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// computeTextGeometry +// +osg::Geometry* computeTextGeometry(osg::Geometry* glyphGeometry, BevelProfile& profile, float width) +{ + osg::Vec3Array* orig_vertices = dynamic_cast(glyphGeometry->getVertexArray()); + if (!orig_vertices) + { + OSG_NOTICE<<"computeTextGeometry(..): No vertices on glyphGeometry."< text_geometry = new osg::Geometry; + osg::ref_ptr vertices = new osg::Vec3Array; + text_geometry->setVertexArray(vertices.get()); + + typedef std::vector Indices; + const unsigned int NULL_VALUE = UINT_MAX; + Indices front_indices, back_indices; + front_indices.resize(orig_vertices->size(), NULL_VALUE); + back_indices.resize(orig_vertices->size(), NULL_VALUE); + + osg::DrawElementsUShort* face = 0; + osg::Geometry::PrimitiveSetList bevelPrimitiveSets; + osg::Vec3 forward(0,0,-width); + + // collect bevels and face primitive sets + for(osg::Geometry::PrimitiveSetList::iterator itr = glyphGeometry->getPrimitiveSetList().begin(); + itr != glyphGeometry->getPrimitiveSetList().end(); + ++itr) + { + osg::PrimitiveSet* prim = itr->get(); + if (prim->getName()=="face") face = dynamic_cast(prim); + else if (prim->getName()=="bevel") bevelPrimitiveSets.push_back(prim); + } + + // if we don't have a face we can't create any 3d text + if (!face) return 0; + + // build up the vertices primitives for the front face, and record the indices + // for later use, and to ensure sharing of vertices in the face primitive set + osg::DrawElementsUShort* frontFace = new osg::DrawElementsUShort(GL_TRIANGLES); + text_geometry->addPrimitiveSet(frontFace); + for(unsigned int i=0; isize();) + { + unsigned int pi = (*face)[i++]; + if (front_indices[pi]==NULL_VALUE) + { + front_indices[pi] = vertices->size(); + vertices->push_back((*orig_vertices)[pi]); + } + frontFace->push_back(front_indices[pi]); + } + + + // build up the vertices primitives for the back face, and record the indices + // for later use, and to ensure sharing of vertices in the face primitive set + // the order of the triangle indices are flipped to make sure that the triangles are back face + osg::DrawElementsUShort* backFace = new osg::DrawElementsUShort(GL_TRIANGLES); + text_geometry->addPrimitiveSet(backFace); + for(unsigned int i=0; isize()-2;) + { + unsigned int p1 = (*face)[i++]; + unsigned int p2 = (*face)[i++]; + unsigned int p3 = (*face)[i++]; + if (back_indices[p1]==NULL_VALUE) + { + back_indices[p1] = vertices->size(); + vertices->push_back((*orig_vertices)[p1]+forward); + } + + if (back_indices[p2]==NULL_VALUE) + { + back_indices[p2] = vertices->size(); + vertices->push_back((*orig_vertices)[p2]+forward); + } + + if (back_indices[p3]==NULL_VALUE) + { + back_indices[p3] = vertices->size(); + vertices->push_back((*orig_vertices)[p3]+forward); + } + + backFace->push_back(back_indices[p1]); + backFace->push_back(back_indices[p3]); + backFace->push_back(back_indices[p2]); + } + + bool shareVerticesWithFaces = true; + + // now build up the bevel + for(osg::Geometry::PrimitiveSetList::iterator itr = bevelPrimitiveSets.begin(); + itr != bevelPrimitiveSets.end(); + ++itr) + { + osg::DrawElementsUShort* bevel = dynamic_cast(itr->get()); + if (!bevel) continue; + + unsigned int no_vertices_on_boundary = bevel->size()/2; + + osgText::BevelProfile::Vertices& profileVertices = profile.getVertices(); + unsigned int no_vertices_on_bevel = profileVertices.size(); + + Indices bevelIndices; + bevelIndices.resize(no_vertices_on_boundary*no_vertices_on_bevel, NULL_VALUE); + + // populate vertices + for(unsigned int i=0; isize(); + vertices->push_back(base_vertex); + } + + bevelIndices[i*no_vertices_on_bevel + 0] = front_indices[basei]; + + for(unsigned int j=1; jsize(); + vertices->push_back(pos); + } + + if (back_indices[basei]==NULL_VALUE) + { + back_indices[basei] = vertices->size(); + vertices->push_back(base_vertex + forward); + } + + bevelIndices[i*no_vertices_on_bevel + no_vertices_on_bevel-1] = back_indices[basei]; + } + else + { + for(unsigned int j=0; jsize(); + vertices->push_back(pos); + } + } + } + + osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(GL_TRIANGLES); + unsigned int base, next; + for(unsigned int i = 0; i< no_vertices_on_boundary-1; ++i) + { + for(unsigned int j=0; jpush_back(bevelIndices[base]); + elements->push_back(bevelIndices[next]); + elements->push_back(bevelIndices[base+1]); + + elements->push_back(bevelIndices[base+1]); + elements->push_back(bevelIndices[next]); + elements->push_back(bevelIndices[next+1]); + } + } + + text_geometry->addPrimitiveSet(elements); + } + + return text_geometry.release(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// computeShellGeometry +// +osg::Geometry* computeShellGeometry(osg::Geometry* glyphGeometry, BevelProfile& profile, float width) +{ + return 0; +} + +} \ No newline at end of file diff --git a/examples/osgtext3D/GlyphGeometry.h b/examples/osgtext3D/GlyphGeometry.h new file mode 100644 index 000000000..6f619956d --- /dev/null +++ b/examples/osgtext3D/GlyphGeometry.h @@ -0,0 +1,53 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGTEXT_GLYPHGEOMETRY +#define OSGTEXT_GLYPHGEOMETRY 1 + +#include + +namespace osgText +{ + +class BevelProfile +{ + public: + + typedef std::vector Vertices; + + BevelProfile(); + + void flatBevel(float width=0.25f); + + void roundedBevel(float width=0.5f, unsigned int numSteps=10); + + void roundedBevel2(float width=0.5f, unsigned int numSteps=10); + + void print(std::ostream& fout); + + Vertices& getVertices() { return _vertices; } + + protected: + + Vertices _vertices; +}; + +extern osg::Geometry* computeGlyphGeometry(osgText::Font3D::Glyph3D* glyph, float bevelThickness, float shellThickness); + +extern osg::Geometry* computeTextGeometry(osg::Geometry* glyphGeometry, BevelProfile& profile, float width); + +extern osg::Geometry* computeShellGeometry(osg::Geometry* glyphGeometry, BevelProfile& profile, float width); + +} + +#endif \ No newline at end of file diff --git a/examples/osgtext3D/osgtext3D.cpp b/examples/osgtext3D/osgtext3D.cpp index 1df06669e..84ff0c573 100644 --- a/examples/osgtext3D/osgtext3D.cpp +++ b/examples/osgtext3D/osgtext3D.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -30,75 +31,12 @@ #include #include +#include "GlyphGeometry.h" + extern int main_orig(int, char**); extern int main_test(int, char**); -class BevelProfile -{ - public: - - typedef std::vector Vertices; - - BevelProfile() - { - flatBevel(); - } - - void flatBevel(float width=0.25f) - { - _vertices.clear(); - - if (width>0.5f) width = 0.5f; - - _vertices.push_back(osg::Vec2(0.0f,0.0f)); - - _vertices.push_back(osg::Vec2(width,1.0f)); - - if (width<0.5f) _vertices.push_back(osg::Vec2(1-width,1.0f)); - - _vertices.push_back(osg::Vec2(1.0f,0.0f)); - } - - void roundedBevel(float width=0.5f, unsigned int numSteps=10) - { - _vertices.clear(); - - if (width>0.5f) width = 0.5f; - - unsigned int i = 0; - for(; i<=numSteps; ++i) - { - float angle = float(osg::PI)*0.5f*(float(i)/float(numSteps)); - _vertices.push_back( osg::Vec2((1.0f-cosf(angle))*width, sinf(angle)) ); - } - - // start the second half one into the curve if the width is half way across - i = width<0.5f ? 0 : 1; - for(; i<=numSteps; ++i) - { - float angle = float(osg::PI)*0.5f*(float(numSteps-i)/float(numSteps)); - _vertices.push_back( osg::Vec2(1.0-(1.0f-cosf(angle))*width, sin(angle)) ); - } - } - - void print(std::ostream& fout) - { - OSG_NOTICE<<"print bevel"< maxThickness) + { + maxThickness = thickness; + maxThickness_i = i; + } + } + + return maxThickness_i != _segments.size(); + } + + + void removeAllSegmentsAboveThickness(float targetThickness) + { + // OSG_NOTICE<<"removeAllSegmentsBelowThickness("<size()/2; - BevelProfile::Vertices& profileVertices = profile.getVertices(); + osgText::BevelProfile::Vertices& profileVertices = profile.getVertices(); unsigned int no_vertices_on_bevel = profileVertices.size(); unsigned int start = new_vertices->size(); @@ -530,6 +499,54 @@ osg::Geometry* computeFrontAndBackGeometry(osg::Geometry* geometry, float width) return new_geometry; } + +osg::Geometry* createShell(osg::Geometry* geometry, float distance) +{ + if (!geometry) return 0; + + osg::Vec3Array* orig_vertices = dynamic_cast(geometry->getVertexArray()); + if (!orig_vertices) return 0; + + osg::Vec3Array* orig_normals = dynamic_cast(geometry->getNormalArray()); + if (!orig_normals) return 0; + + if (orig_vertices->size() != orig_normals->size()) return 0; + + osg::Geometry* new_geometry = new osg::Geometry; + osg::Vec3Array* new_vertices = new osg::Vec3Array(orig_vertices->size()); + for(unsigned int i=0; isize(); ++i) + { + (*new_vertices)[i] = (*orig_vertices)[i] + (*orig_normals)[i]*distance; + } + + new_geometry->setVertexArray(new_vertices); + + osg::Vec4Array* new_colours = new osg::Vec4Array; + new_colours->push_back(osg::Vec4(1.0,1.0,1.0,0.2)); + new_geometry->setColorArray(new_colours); + new_geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + + osg::TriangleIndexFunctor ctif; + geometry->accept(ctif); + + // front face + osg::DrawElementsUInt* shell = new osg::DrawElementsUInt(GL_TRIANGLES); + new_geometry->addPrimitiveSet(shell); + CollectTriangleIndicesFunctor::Indices& face = ctif._indices; + for(unsigned int i=0; ipush_back(face[i]); + } + + osg::StateSet* stateset = new_geometry->getOrCreateStateSet(); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateset->setAttributeAndModes(new osg::CullFace, osg::StateAttribute::ON); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + return new_geometry; +} + + osg::Geometry* computeThickness(osg::Geometry* orig_geometry, float thickness) { // OSG_NOTICE<<"computeThickness("<getMode()==GL_POLYGON) { Boundary boundary(orig_vertices, drawArray->getFirst(), drawArray->getCount()); - boundary.removeAllSegmentsBelowThickness(thickness); + if (thickness>0.0f) boundary.removeAllSegmentsBelowThickness(thickness); + else if (thickness<0.0f) boundary.removeAllSegmentsAboveThickness(thickness); boundary.addBoundaryToGeometry(new_geometry, thickness); } } @@ -601,9 +619,10 @@ int main(int argc, char** argv) OSG_NOTICE<<"creaseAngle="< geode = new osg::Geode; +#if 1 + osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph.get(), thickness, width); + osg::ref_ptr textGeometry = osgText::computeTextGeometry(glyphGeometry.get(), profile, width); + osg::ref_ptr shellGeometry = osgText::computeShellGeometry(glyphGeometry.get(), profile, width); + if (textGeometry.valid()) + { + geode->addDrawable(textGeometry.get()); + // create the normals + if (true) + { + osgUtil::SmoothingVisitor smoother; + smoother.setCreaseAngle(osg::DegreesToRadians(creaseAngle)); + geode->accept(smoother); + } + } + + if (shellGeometry.valid()) geode->addDrawable(shellGeometry.get()); +#else osg::Vec3Array* vertices = glyph->getRawVertexArray(); osg::Geometry::PrimitiveSetList& primitives = glyph->getRawFacePrimitiveSetList(); @@ -661,9 +698,25 @@ int main(int argc, char** argv) geode->addDrawable(bevel.get()); } - osgUtil::SmoothingVisitor smoother; - smoother.setCreaseAngle(osg::DegreesToRadians(creaseAngle)); - geode->accept(smoother); + // create the normals + { + osgUtil::SmoothingVisitor smoother; + geode->accept(smoother); + } + + osg::ref_ptr shell = createShell(bevel.get(), width); + + { + osgUtil::SmoothingVisitor smoother; + smoother.setCreaseAngle(osg::DegreesToRadians(creaseAngle)); + geode->accept(smoother); + } + + if (shell.valid()) + { + geode->addDrawable(shell.get()); + } +#endif transform->addChild(geode.get());