Moved TextNode into osgText.
Cleaned up freetype plugin so it no longer does tesselation - instead Glyph and TextNode do this.
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
#this file is automatically generated
|
||||
|
||||
SET(TARGET_H
|
||||
GlyphGeometry.h
|
||||
TextNode.h
|
||||
)
|
||||
|
||||
SET(TARGET_SRC
|
||||
GlyphGeometry.cpp
|
||||
TextNode.cpp
|
||||
osgtext3D_orig.cpp
|
||||
osgtext3D_test.cpp
|
||||
osgtext3D.cpp
|
||||
|
||||
@@ -35,32 +35,48 @@ public:
|
||||
typedef std::pair<unsigned int, unsigned int> Segment;
|
||||
typedef std::vector<Segment> Segments;
|
||||
osg::ref_ptr<osg::Vec3Array> _vertices;
|
||||
unsigned int _start;
|
||||
unsigned int _count;
|
||||
osg::ref_ptr<osg::DrawElementsUShort> _elements;
|
||||
Segments _segments;
|
||||
|
||||
Boundary(osg::Vec3Array* vertices, unsigned int start, unsigned int count)
|
||||
Boundary(osg::Vec3Array* vertices, osg::PrimitiveSet* primitiveSet)
|
||||
{
|
||||
_vertices = vertices;
|
||||
_start = start;
|
||||
_count = count;
|
||||
|
||||
if ((*_vertices)[start]==(*_vertices)[start+count-1])
|
||||
osg::DrawArrays* drawArrays = dynamic_cast<osg::DrawArrays*>(primitiveSet);
|
||||
if (drawArrays)
|
||||
{
|
||||
// OSG_NOTICE<<"Boundary is a line loop"<<std::endl;
|
||||
set(vertices, drawArrays->getFirst(), drawArrays->getCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_NOTICE<<"Boundary is not a line loop"<<std::endl;
|
||||
osg::DrawElementsUShort* elements = dynamic_cast<osg::DrawElementsUShort*>(primitiveSet);
|
||||
if (elements) set(vertices, elements);
|
||||
}
|
||||
}
|
||||
|
||||
_segments.reserve(count-1);
|
||||
for(unsigned int i=start; i<start+count-2; ++i)
|
||||
void set(osg::Vec3Array* vertices, unsigned int start, unsigned int count)
|
||||
{
|
||||
osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::POLYGON);
|
||||
for(unsigned int i=start; i<start+count; ++i)
|
||||
{
|
||||
_segments.push_back(Segment(i,i+1));
|
||||
elements->push_back(i);
|
||||
}
|
||||
_segments.push_back(Segment(start+count-2,start));
|
||||
|
||||
set(vertices, elements);
|
||||
}
|
||||
|
||||
void set(osg::Vec3Array* vertices, osg::DrawElementsUShort* elements)
|
||||
{
|
||||
_vertices = vertices;
|
||||
_elements = elements;
|
||||
|
||||
_segments.clear();
|
||||
|
||||
if (elements->empty()) return;
|
||||
|
||||
_segments.reserve(elements->size()-1);
|
||||
for(unsigned int i=0; i<elements->size()-1; ++i)
|
||||
{
|
||||
_segments.push_back(Segment((*elements)[i],(*elements)[i+1]));
|
||||
}
|
||||
}
|
||||
|
||||
osg::Vec3 computeRayIntersectionPoint(const osg::Vec3& a, const osg::Vec3& an, const osg::Vec3& c, const osg::Vec3& cn)
|
||||
@@ -251,7 +267,7 @@ public:
|
||||
face->setName("face");
|
||||
|
||||
// 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);
|
||||
new_vertices->reserve(new_vertices->size() + _segments.size()+1 + _elements->size());
|
||||
|
||||
// create vertices
|
||||
unsigned int previous_second = _segments[0].second;
|
||||
@@ -259,10 +275,13 @@ public:
|
||||
unsigned int first = new_vertices->size();
|
||||
new_vertices->push_back(newPoint);
|
||||
|
||||
if (_segments[0].first != _start)
|
||||
unsigned int start = (*_elements)[0];
|
||||
unsigned int count = _elements->size();
|
||||
|
||||
if (_segments[0].first != start)
|
||||
{
|
||||
//OSG_NOTICE<<"We have pruned from the start"<<std::endl;
|
||||
for(unsigned int j=_start; j<=_segments[0].first;++j)
|
||||
for(unsigned int j=start; j<=_segments[0].first;++j)
|
||||
{
|
||||
face->push_back(first);
|
||||
}
|
||||
@@ -298,7 +317,7 @@ public:
|
||||
// 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)
|
||||
while(face->size() < count)
|
||||
{
|
||||
face->push_back(first);
|
||||
}
|
||||
@@ -313,11 +332,11 @@ public:
|
||||
|
||||
osg::DrawElementsUShort* bevel = new osg::DrawElementsUShort(GL_QUAD_STRIP);
|
||||
bevel->setName("bevel");
|
||||
bevel->reserve(_count*2);
|
||||
for(unsigned int i=0; i<_count; ++i)
|
||||
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]);
|
||||
new_vertices->push_back((*_vertices)[(*_elements)[i]]);
|
||||
bevel->push_back(vi);
|
||||
bevel->push_back((*face)[i]);
|
||||
}
|
||||
@@ -328,6 +347,9 @@ public:
|
||||
{
|
||||
if (_segments.empty()) return;
|
||||
|
||||
unsigned int start = (*_elements)[0];
|
||||
unsigned int count = _elements->size();
|
||||
|
||||
if (geometry->getVertexArray()==0) geometry->setVertexArray(new osg::Vec3Array(*_vertices));
|
||||
osg::Vec3Array* new_vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
|
||||
|
||||
@@ -336,7 +358,7 @@ public:
|
||||
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);
|
||||
new_vertices->reserve(new_vertices->size() + _segments.size()+1 + count);
|
||||
|
||||
// create vertices
|
||||
unsigned int previous_second = _segments[0].second;
|
||||
@@ -344,10 +366,10 @@ public:
|
||||
unsigned int first = new_vertices->size();
|
||||
new_vertices->push_back(newPoint);
|
||||
|
||||
if (_segments[0].first != _start)
|
||||
if (_segments[0].first != start)
|
||||
{
|
||||
//OSG_NOTICE<<"We have pruned from the start"<<std::endl;
|
||||
for(unsigned int j=_start; j<=_segments[0].first;++j)
|
||||
for(unsigned int j=start; j<=_segments[0].first;++j)
|
||||
{
|
||||
face->push_back(first);
|
||||
}
|
||||
@@ -383,7 +405,7 @@ public:
|
||||
// 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)
|
||||
while(face->size() < count)
|
||||
{
|
||||
face->push_back(first);
|
||||
}
|
||||
@@ -396,10 +418,10 @@ public:
|
||||
|
||||
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->reserve(count*2);
|
||||
for(unsigned int i=0; i<count; ++i)
|
||||
{
|
||||
bevel->push_back(_start+i);
|
||||
bevel->push_back((*_elements)[i]);
|
||||
bevel->push_back((*face)[i]);
|
||||
}
|
||||
geometry->addPrimitiveSet(bevel);
|
||||
@@ -444,24 +466,24 @@ osg::Geometry* computeGlyphGeometry(osgText::Glyph3D* glyph, float bevelThicknes
|
||||
itr != orig_primitives.end();
|
||||
++itr)
|
||||
{
|
||||
osg::DrawArrays* drawArray = dynamic_cast<osg::DrawArrays*>(itr->get());
|
||||
if (drawArray && drawArray->getMode()==GL_POLYGON)
|
||||
if ((*itr)->getMode()==GL_POLYGON)
|
||||
{
|
||||
Boundary boundaryInner(orig_vertices, drawArray->getFirst(), drawArray->getCount());
|
||||
Boundary boundaryInner(orig_vertices, itr->get());
|
||||
boundaryInner.removeAllSegmentsBelowThickness(bevelThickness);
|
||||
boundaryInner.newAddBoundaryToGeometry(new_geometry, bevelThickness, "face", "bevel");
|
||||
|
||||
Boundary boundaryOuter(orig_vertices, drawArray->getFirst(), drawArray->getCount());
|
||||
Boundary boundaryOuter(orig_vertices, itr->get());
|
||||
boundaryOuter.removeAllSegmentsAboveThickness(-shellThickness);
|
||||
boundaryOuter.newAddBoundaryToGeometry(new_geometry, -shellThickness, "", "shell");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(new_geometry->getVertexArray());
|
||||
|
||||
// need to tessellate the inner boundary
|
||||
{
|
||||
osg::Geometry* face_geometry = new osg::Geometry;
|
||||
osg::ref_ptr<osg::Geometry> face_geometry = new osg::Geometry;
|
||||
face_geometry->setVertexArray(vertices);
|
||||
|
||||
osg::CopyOp copyop(osg::CopyOp::DEEP_COPY_ALL);
|
||||
@@ -510,6 +532,123 @@ osg::Geometry* computeGlyphGeometry(osgText::Glyph3D* glyph, float bevelThicknes
|
||||
return new_geometry.release();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// computeTextGeometry
|
||||
//
|
||||
osg::Geometry* computeTextGeometry(osgText::Glyph3D* glyph, float width)
|
||||
{
|
||||
osg::Vec3Array* orig_vertices = glyph->getRawVertexArray();
|
||||
osg::Geometry::PrimitiveSetList& orig_primitives = glyph->getRawFacePrimitiveSetList();
|
||||
|
||||
osg::ref_ptr<osg::Geometry> text_geometry = new osg::Geometry;
|
||||
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array((*orig_vertices));
|
||||
|
||||
text_geometry->setVertexArray(vertices.get());
|
||||
text_geometry->setPrimitiveSetList(orig_primitives);
|
||||
|
||||
osgUtil::Tessellator ts;
|
||||
ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
|
||||
ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
|
||||
ts.retessellatePolygons(*text_geometry);
|
||||
|
||||
osg::TriangleIndexFunctor<CollectTriangleIndicesFunctor> ctif;
|
||||
text_geometry->accept(ctif);
|
||||
CollectTriangleIndicesFunctor::Indices& indices = ctif._indices;
|
||||
|
||||
// remove the previous primitive sets
|
||||
text_geometry->getPrimitiveSetList().clear();
|
||||
|
||||
if (indices.empty()) return 0;
|
||||
|
||||
|
||||
// create a front face using triangle indices
|
||||
osg::DrawElementsUShort* frontFace = new osg::DrawElementsUShort(GL_TRIANGLES);
|
||||
frontFace->setName("face");
|
||||
text_geometry->addPrimitiveSet(frontFace);
|
||||
for(unsigned int i=0; i<indices.size();++i)
|
||||
{
|
||||
frontFace->push_back(indices[i]);
|
||||
}
|
||||
|
||||
typedef std::vector<unsigned int> Indices;
|
||||
const unsigned int NULL_VALUE = UINT_MAX;
|
||||
Indices back_indices;
|
||||
back_indices.resize(vertices->size(), NULL_VALUE);
|
||||
osg::Vec3 forward(0,0,-width);
|
||||
|
||||
// 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; i<indices.size()-2;)
|
||||
{
|
||||
unsigned int p1 = indices[i++];
|
||||
unsigned int p2 = indices[i++];
|
||||
unsigned int p3 = indices[i++];
|
||||
if (back_indices[p1]==NULL_VALUE)
|
||||
{
|
||||
back_indices[p1] = vertices->size();
|
||||
vertices->push_back((*vertices)[p1]+forward);
|
||||
}
|
||||
|
||||
if (back_indices[p2]==NULL_VALUE)
|
||||
{
|
||||
back_indices[p2] = vertices->size();
|
||||
vertices->push_back((*vertices)[p2]+forward);
|
||||
}
|
||||
|
||||
if (back_indices[p3]==NULL_VALUE)
|
||||
{
|
||||
back_indices[p3] = vertices->size();
|
||||
vertices->push_back((*vertices)[p3]+forward);
|
||||
}
|
||||
|
||||
backFace->push_back(back_indices[p1]);
|
||||
backFace->push_back(back_indices[p3]);
|
||||
backFace->push_back(back_indices[p2]);
|
||||
}
|
||||
|
||||
unsigned int orig_size = orig_vertices->size();
|
||||
Indices frontedge_indices, backedge_indices;
|
||||
frontedge_indices.resize(orig_size, NULL_VALUE);
|
||||
backedge_indices.resize(orig_size, NULL_VALUE);
|
||||
|
||||
|
||||
for(osg::Geometry::PrimitiveSetList::iterator itr = orig_primitives.begin();
|
||||
itr != orig_primitives.end();
|
||||
++itr)
|
||||
{
|
||||
osg::DrawElementsUShort* edging = new osg::DrawElementsUShort(osg::PrimitiveSet::QUAD_STRIP);
|
||||
text_geometry->addPrimitiveSet(edging);
|
||||
|
||||
osg::DrawElementsUShort* elements = dynamic_cast<osg::DrawElementsUShort*>(itr->get());
|
||||
if (elements)
|
||||
{
|
||||
for(unsigned int i=0; i<elements->size(); ++i)
|
||||
{
|
||||
unsigned int ei = (*elements)[i];
|
||||
if (frontedge_indices[ei]==NULL_VALUE)
|
||||
{
|
||||
frontedge_indices[ei] = vertices->size();
|
||||
vertices->push_back((*orig_vertices)[ei]);
|
||||
}
|
||||
if (backedge_indices[ei]==NULL_VALUE)
|
||||
{
|
||||
backedge_indices[ei] = vertices->size();
|
||||
vertices->push_back((*orig_vertices)[ei]+forward);
|
||||
}
|
||||
|
||||
edging->push_back(backedge_indices[ei]);
|
||||
edging->push_back(frontedge_indices[ei]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return text_geometry.release();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// computeTextGeometry
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace osgText
|
||||
|
||||
extern osg::Geometry* computeGlyphGeometry(osgText::Glyph3D* glyph, float bevelThickness, float shellThickness);
|
||||
|
||||
extern osg::Geometry* computeTextGeometry(osgText::Glyph3D* glyph, float width);
|
||||
|
||||
extern osg::Geometry* computeTextGeometry(osg::Geometry* glyphGeometry, const Bevel& profile, float width);
|
||||
|
||||
extern osg::Geometry* computeShellGeometry(osg::Geometry* glyphGeometry, const Bevel& profile, float width);
|
||||
|
||||
@@ -335,6 +335,14 @@ void TextTechnique::addCharacter(const osg::Vec3& position, const osg::Vec3& siz
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> textGeometry = osgText::computeTextGeometry(glyph, width);
|
||||
if (textGeometry.valid()) geode->addDrawable(textGeometry.get());
|
||||
|
||||
// create the normals
|
||||
if (smooth && textGeometry.valid())
|
||||
{
|
||||
osgUtil::SmoothingVisitor::smooth(*textGeometry, osg::DegreesToRadians(creaseAngle));
|
||||
}
|
||||
}
|
||||
|
||||
transform->addChild(geode.get());
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <osg/TriangleIndexFunctor>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osgUtil/SmoothingVisitor>
|
||||
#include <osgText/Font3D>
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgGA/StateSetManipulator>
|
||||
#include <osgUtil/Tessellator>
|
||||
@@ -31,113 +30,11 @@
|
||||
#include <osgViewer/ViewerEventHandlers>
|
||||
#include <osg/io_utils>
|
||||
|
||||
#include "GlyphGeometry.h"
|
||||
#include "TextNode.h"
|
||||
#include <osgText/TextNode>
|
||||
|
||||
extern int main_orig(int, char**);
|
||||
extern int main_test(int, char**);
|
||||
|
||||
int main_experimental(osg::ArgumentParser& arguments)
|
||||
{
|
||||
std::string fontFile("arial.ttf");
|
||||
while(arguments.read("-f",fontFile)) {}
|
||||
|
||||
std::string word("This is a simple test");
|
||||
|
||||
while(arguments.read("--ascii"))
|
||||
{
|
||||
word.clear();
|
||||
for(unsigned int c=' '; c<=127;++c)
|
||||
{
|
||||
word.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
while(arguments.read("-w",word)) {}
|
||||
|
||||
osg::ref_ptr<osgText::Font> font = osgText::readFontFile(fontFile);
|
||||
if (!font) return 1;
|
||||
OSG_NOTICE<<"Read font "<<fontFile<<" font="<<font.get()<<std::endl;
|
||||
|
||||
bool useTessellator = true;
|
||||
while(arguments.read("-t") || arguments.read("--tessellate")) { useTessellator = true; }
|
||||
while(arguments.read("--no-tessellate")) { useTessellator = false; }
|
||||
|
||||
float thickness = 5.0;
|
||||
while(arguments.read("--thickness",thickness)) {}
|
||||
|
||||
float width = 20.0;
|
||||
while(arguments.read("--width",width)) {}
|
||||
|
||||
float creaseAngle = 30.0f;
|
||||
while(arguments.read("--crease-angle",creaseAngle)) {}
|
||||
|
||||
OSG_NOTICE<<"creaseAngle="<<creaseAngle<<std::endl;
|
||||
|
||||
osgText::Bevel profile;
|
||||
float ratio = 0.5;
|
||||
while(arguments.read("--rounded",ratio)) { profile.roundedBevel(ratio); }
|
||||
while(arguments.read("--rounded2",ratio)) { profile.roundedBevel2(ratio); }
|
||||
while(arguments.read("--flat",ratio)) { profile.flatBevel(ratio); }
|
||||
|
||||
bool outline = false;
|
||||
while(arguments.read("--outline")) { outline = true; }
|
||||
while(arguments.read("--no-outline")) { outline = false; }
|
||||
|
||||
bool smooth = true;
|
||||
while(arguments.read("--flat-shaded")) { smooth = false; }
|
||||
while(arguments.read("--smooth")) { smooth = false; }
|
||||
|
||||
unsigned int numSamples = 10;
|
||||
while(arguments.read("--samples", numSamples)) {}
|
||||
font->setNumberCurveSamples(numSamples);
|
||||
|
||||
profile.print(std::cout);
|
||||
|
||||
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
osg::Vec3 position;
|
||||
|
||||
for(unsigned int i=0; i<word.size(); ++i)
|
||||
{
|
||||
osg::ref_ptr<osgText::Glyph3D> glyph = font->getGlyph3D(word[i]);
|
||||
if (!glyph) return 1;
|
||||
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
|
||||
transform->setPosition(position);
|
||||
transform->setAttitude(osg::Quat(osg::inDegrees(90.0),osg::Vec3d(1.0,0.0,0.0)));
|
||||
|
||||
position.x() += glyph->getHorizontalWidth();
|
||||
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> glyphGeometry = osgText::computeGlyphGeometry(glyph.get(), thickness, width);
|
||||
osg::ref_ptr<osg::Geometry> textGeometry = osgText::computeTextGeometry(glyphGeometry.get(), profile, width);
|
||||
osg::ref_ptr<osg::Geometry> shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), profile, width) : 0;
|
||||
if (textGeometry.valid()) geode->addDrawable(textGeometry.get());
|
||||
if (shellGeometry.valid()) geode->addDrawable(shellGeometry.get());
|
||||
|
||||
// create the normals
|
||||
if (smooth && textGeometry.valid())
|
||||
{
|
||||
osgUtil::SmoothingVisitor::smooth(*textGeometry, osg::DegreesToRadians(creaseAngle));
|
||||
}
|
||||
|
||||
transform->addChild(geode.get());
|
||||
|
||||
group->addChild(transform.get());
|
||||
}
|
||||
|
||||
std::string filename;
|
||||
if (arguments.read("-o", filename)) osgDB::writeNodeFile(*group, filename);
|
||||
|
||||
osgViewer::Viewer viewer(arguments);
|
||||
viewer.setSceneData(group.get());
|
||||
viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
|
||||
viewer.addEventHandler(new osgViewer::StatsHandler);
|
||||
return viewer.run();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
osg::ArgumentParser arguments(&argc, argv);
|
||||
@@ -150,10 +47,6 @@ int main(int argc, char** argv)
|
||||
{
|
||||
return main_orig(argc,argv);
|
||||
}
|
||||
else if (arguments.read("--exp"))
|
||||
{
|
||||
return main_experimental(arguments);
|
||||
}
|
||||
|
||||
osgViewer::Viewer viewer(arguments);
|
||||
|
||||
@@ -184,6 +77,10 @@ int main(int argc, char** argv)
|
||||
|
||||
style->setBevel(bevel);
|
||||
|
||||
// set up outline.
|
||||
while(arguments.read("--outline",r)) { style->setOutlineRatio(r); }
|
||||
|
||||
|
||||
osgText::TextNode* text = new osgText::TextNode;
|
||||
text->setText(word);
|
||||
text->setFont(font.get());
|
||||
|
||||
@@ -17,241 +17,16 @@
|
||||
#include <string>
|
||||
#include <istream>
|
||||
|
||||
#include <osg/Vec2>
|
||||
#include <osg/Image>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/buffered_value>
|
||||
#include <osg/TexEnv>
|
||||
#include <osg/Geometry>
|
||||
#include <osgDB/ReaderWriter>
|
||||
|
||||
#include <osgText/Export>
|
||||
#include <osgText/KerningType>
|
||||
#include <osgText/Glyph>
|
||||
#include <osgDB/Options>
|
||||
|
||||
#include <OpenThreads/Mutex>
|
||||
|
||||
namespace osgText {
|
||||
|
||||
// forward declare Font
|
||||
class Font;
|
||||
class Text;
|
||||
class Glyph3D;
|
||||
class GlyphTexture;
|
||||
|
||||
class OSGTEXT_EXPORT Glyph : public osg::Image
|
||||
{
|
||||
public:
|
||||
|
||||
Glyph(unsigned int glyphCode);
|
||||
|
||||
unsigned int getGlyphCode() const { return _glyphCode; }
|
||||
|
||||
void setHorizontalBearing(const osg::Vec2& bearing);
|
||||
const osg::Vec2& getHorizontalBearing() const;
|
||||
|
||||
void setHorizontalAdvance(float advance);
|
||||
float getHorizontalAdvance() const;
|
||||
|
||||
void setVerticalBearing(const osg::Vec2& bearing);
|
||||
const osg::Vec2& getVerticalBearing() const;
|
||||
|
||||
void setVerticalAdvance(float advance);
|
||||
float getVerticalAdvance() const;
|
||||
|
||||
void setTexture(GlyphTexture* texture);
|
||||
GlyphTexture* getTexture();
|
||||
const GlyphTexture* getTexture() const;
|
||||
|
||||
void setTexturePosition(int posX,int posY);
|
||||
int getTexturePositionX() const;
|
||||
int getTexturePositionY() const;
|
||||
|
||||
void setMinTexCoord(const osg::Vec2& coord);
|
||||
const osg::Vec2& getMinTexCoord() const;
|
||||
|
||||
void setMaxTexCoord(const osg::Vec2& coord);
|
||||
const osg::Vec2& getMaxTexCoord() const;
|
||||
|
||||
void subload() const;
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~Glyph();
|
||||
|
||||
Font* _font;
|
||||
unsigned int _glyphCode;
|
||||
|
||||
osg::Vec2 _horizontalBearing;
|
||||
float _horizontalAdvance;
|
||||
|
||||
osg::Vec2 _verticalBearing;
|
||||
float _verticalAdvance;
|
||||
|
||||
GlyphTexture* _texture;
|
||||
int _texturePosX;
|
||||
int _texturePosY;
|
||||
osg::Vec2 _minTexCoord;
|
||||
osg::Vec2 _maxTexCoord;
|
||||
|
||||
typedef osg::buffered_value<GLuint> GLObjectList;
|
||||
mutable GLObjectList _globjList;
|
||||
|
||||
};
|
||||
|
||||
class OSGTEXT_EXPORT Glyph3D : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
Glyph3D(unsigned int glyphCode):
|
||||
osg::Referenced(true),
|
||||
_glyphCode(glyphCode),
|
||||
_horizontalBearing(0,0),
|
||||
_horizontalAdvance(0),
|
||||
_verticalBearing(0,0),
|
||||
_verticalAdvance(0)
|
||||
{}
|
||||
|
||||
unsigned int getGlyphCode() const { return _glyphCode; }
|
||||
|
||||
void setHorizontalBearing(const osg::Vec2& bearing) { _horizontalBearing=bearing; }
|
||||
const osg::Vec2 & getHorizontalBearing() const { return _horizontalBearing; }
|
||||
|
||||
void setHorizontalAdvance(float advance) { _horizontalAdvance=advance; }
|
||||
float getHorizontalAdvance() const { return _horizontalAdvance; }
|
||||
|
||||
void setVerticalBearing(const osg::Vec2& bearing) { _verticalBearing=bearing; }
|
||||
const osg::Vec2& getVerticalBearing() const { return _verticalBearing; }
|
||||
|
||||
void setVerticalAdvance(float advance) { _verticalAdvance=advance; }
|
||||
float getVerticalAdvance() const { return _verticalAdvance; }
|
||||
|
||||
void setBoundingBox(osg::BoundingBox & bb) { _bb=bb; }
|
||||
const osg::BoundingBox & getBoundingBox() const { return _bb; }
|
||||
|
||||
|
||||
/** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/
|
||||
virtual void setThreadSafeRefUnref(bool threadSafe);
|
||||
|
||||
|
||||
void setRawVertexArray(osg::Vec3Array* vertices) { _rawVertexArray = vertices; }
|
||||
osg::Vec3Array* getRawVertexArray() { return _rawVertexArray.get(); }
|
||||
|
||||
/** Get the PrimitiveSetList for the raw face which hasn't been tessellated. */
|
||||
osg::Geometry::PrimitiveSetList & getRawFacePrimitiveSetList() { return _rawFacePrimitiveSetList; }
|
||||
|
||||
/** Get the PrimitiveSetList for the front face. */
|
||||
osg::Geometry::PrimitiveSetList & getFrontPrimitiveSetList() { return _frontPrimitiveSetList; }
|
||||
/** Get the PrimitiveSetList for the wall face. */
|
||||
osg::Geometry::PrimitiveSetList & getWallPrimitiveSetList() { return _wallPrimitiveSetList; }
|
||||
/** Get et the PrimitiveSetList for the back face. */
|
||||
osg::Geometry::PrimitiveSetList & getBackPrimitiveSetList() { return _backPrimitiveSetList; }
|
||||
|
||||
/** Set the VertexArray of the glyph. */
|
||||
void setVertexArray(osg::Vec3Array * va) { _vertexArray = va; }
|
||||
/** Get the VertexArray of the glyph. */
|
||||
osg::Vec3Array * getVertexArray() { return _vertexArray.get(); }
|
||||
/** Set the VertexArray of the glyph. */
|
||||
void setNormalArray(osg::Vec3Array * na) { _normalArray = na; }
|
||||
/** Get the NormalArray for the wall face. */
|
||||
osg::Vec3Array * getNormalArray() { return _normalArray.get(); }
|
||||
|
||||
float getHorizontalWidth() { return (-_horizontalBearing.x() + _horizontalAdvance); }
|
||||
float getHorizontalHeight() { return (-_horizontalBearing.y() + _bb.yMax()); }
|
||||
float getVerticalWidth() { return (-_verticalBearing.x() + _bb.xMax()); }
|
||||
float getVerticalHeight() { return (-_verticalBearing.y() + _verticalAdvance); }
|
||||
|
||||
void setWidth(float width) { _width = width; }
|
||||
float getWidth() { return _width; }
|
||||
|
||||
void setHeight(float height) { _height = height; }
|
||||
float getHeight() { return _height; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~Glyph3D() {}
|
||||
|
||||
unsigned int _glyphCode;
|
||||
|
||||
osg::Vec2 _horizontalBearing;
|
||||
float _horizontalAdvance;
|
||||
|
||||
osg::Vec2 _verticalBearing;
|
||||
float _verticalAdvance;
|
||||
|
||||
osg::BoundingBox _bb;
|
||||
// osg::Vec2 _advance;
|
||||
|
||||
float _width;
|
||||
float _height;
|
||||
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> _vertexArray;
|
||||
osg::ref_ptr<osg::Vec3Array> _normalArray;
|
||||
|
||||
|
||||
osg::Geometry::PrimitiveSetList _frontPrimitiveSetList;
|
||||
osg::Geometry::PrimitiveSetList _wallPrimitiveSetList;
|
||||
osg::Geometry::PrimitiveSetList _backPrimitiveSetList;
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> _rawVertexArray;
|
||||
osg::Geometry::PrimitiveSetList _rawFacePrimitiveSetList;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class OSGTEXT_EXPORT GlyphTexture : public osg::Texture2D
|
||||
{
|
||||
public:
|
||||
|
||||
GlyphTexture();
|
||||
|
||||
const char* className() const { return "GlyphTexture"; }
|
||||
|
||||
/** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/
|
||||
virtual int compare(const osg::StateAttribute& rhs) const;
|
||||
|
||||
/** Set the margin around each glyph, to ensure that texture filtering doesn't bleed adjacent glyph's into each other.*/
|
||||
void setGlyphImageMargin(unsigned int margin) { _margin = margin; }
|
||||
unsigned int getGlyphImageMargin() const { return _margin; }
|
||||
|
||||
void setGlyphImageMarginRatio(float margin) { _marginRatio = margin; }
|
||||
float getGlyphImageMarginRatio() const { return _marginRatio; }
|
||||
|
||||
bool getSpaceForGlyph(Glyph* glyph, int& posX, int& posY);
|
||||
|
||||
void addGlyph(Glyph* glyph,int posX, int posY);
|
||||
|
||||
virtual void apply(osg::State& state) const;
|
||||
|
||||
/** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/
|
||||
virtual void setThreadSafeRefUnref(bool threadSafe);
|
||||
|
||||
/** Resize any per context GLObject buffers to specified size. */
|
||||
virtual void resizeGLObjectBuffers(unsigned int maxSize);
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~GlyphTexture();
|
||||
|
||||
|
||||
// parameter used to compute the size and position of empty space
|
||||
// in the texture which could accommodate new glyphs.
|
||||
int _margin;
|
||||
float _marginRatio;
|
||||
int _usedY;
|
||||
int _partUsedX;
|
||||
int _partUsedY;
|
||||
|
||||
typedef std::vector< osg::ref_ptr<Glyph> > GlyphRefList;
|
||||
typedef std::vector< const Glyph* > GlyphPtrList;
|
||||
typedef osg::buffered_object< GlyphPtrList > GlyphBuffer;
|
||||
|
||||
GlyphRefList _glyphs;
|
||||
mutable GlyphBuffer _glyphsToSubload;
|
||||
|
||||
mutable OpenThreads::Mutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
/** Read a font from specified file. The filename may contain a path.
|
||||
* It will search for the font file in the following places in this order:
|
||||
@@ -272,14 +47,14 @@ protected:
|
||||
* If the given file could not be found, the path part will be stripped and
|
||||
* the file will be searched again in the OS specific directories.
|
||||
*/
|
||||
extern OSGTEXT_EXPORT Font* readFontFile(const std::string& filename, const osgDB::ReaderWriter::Options* userOptions = 0);
|
||||
extern OSGTEXT_EXPORT Font* readFontFile(const std::string& filename, const osgDB::Options* userOptions = 0);
|
||||
|
||||
/** read a font from specified stream.*/
|
||||
extern OSGTEXT_EXPORT Font* readFontStream(std::istream& stream, const osgDB::ReaderWriter::Options* userOptions = 0);
|
||||
extern OSGTEXT_EXPORT Font* readFontStream(std::istream& stream, const osgDB::Options* userOptions = 0);
|
||||
|
||||
extern OSGTEXT_EXPORT osg::ref_ptr<Font> readRefFontFile(const std::string& filename, const osgDB::ReaderWriter::Options* userOptions = 0);
|
||||
extern OSGTEXT_EXPORT osg::ref_ptr<Font> readRefFontFile(const std::string& filename, const osgDB::Options* userOptions = 0);
|
||||
|
||||
extern OSGTEXT_EXPORT osg::ref_ptr<Font> readRefFontStream(std::istream& stream, const osgDB::ReaderWriter::Options* userOptions = 0);
|
||||
extern OSGTEXT_EXPORT osg::ref_ptr<Font> readRefFontStream(std::istream& stream, const osgDB::Options* userOptions = 0);
|
||||
|
||||
extern OSGTEXT_EXPORT std::string findFontFile(const std::string& str);
|
||||
|
||||
|
||||
229
include/osgText/TextNode
Normal file
229
include/osgText/TextNode
Normal file
@@ -0,0 +1,229 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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_TEXTNODE
|
||||
#define OSGTEXT_TEXTNODE 1
|
||||
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/Quat>
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <osgText/Font>
|
||||
#include <osgText/String>
|
||||
|
||||
namespace osgText {
|
||||
|
||||
// forward declare
|
||||
class TextNode;
|
||||
class Glyph;
|
||||
|
||||
class Bevel : public osg::Object
|
||||
{
|
||||
public:
|
||||
|
||||
Bevel();
|
||||
Bevel(const Bevel& bevel, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
META_Object(osgText, Bevel)
|
||||
|
||||
void setBevelThickness(float thickness) { _thickness = thickness; }
|
||||
float getBevelThickness() const { return _thickness; }
|
||||
|
||||
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);
|
||||
|
||||
typedef std::vector<osg::Vec2> Vertices;
|
||||
|
||||
void setVertices(const Vertices& vertices) { _vertices = vertices; }
|
||||
Vertices& getVertices() { return _vertices; }
|
||||
const Vertices& getVertices() const { return _vertices; }
|
||||
|
||||
void print(std::ostream& fout);
|
||||
|
||||
protected:
|
||||
|
||||
float _thickness;
|
||||
Vertices _vertices;
|
||||
};
|
||||
|
||||
|
||||
class Style : public osg::Object
|
||||
{
|
||||
public:
|
||||
|
||||
Style();
|
||||
Style(const Style& style, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
META_Object(osgText, Style)
|
||||
|
||||
/// default Layout implementation used if no other is specified on TextNode
|
||||
static osg::ref_ptr<Style>& getDefaultStyle();
|
||||
|
||||
/// NULL is no bevel
|
||||
void setBevel(Bevel* bevel) { _bevel = bevel; }
|
||||
const Bevel* getBevel() const { return _bevel.get(); }
|
||||
|
||||
|
||||
/// 1 is the default width of the text
|
||||
void setWidthRatio(float widthRatio) { _widthRatio = widthRatio; }
|
||||
float getWidthRatio() const { return _widthRatio; }
|
||||
|
||||
/// 0 is 2D text
|
||||
void setThicknessRatio(float thicknessRatio) { _thicknessRatio = thicknessRatio; }
|
||||
float getThicknessRatio() const { return _thicknessRatio; }
|
||||
|
||||
/// 0 is off
|
||||
void setOutlineRatio(float outlineRatio) { _outlineRatio = outlineRatio; }
|
||||
float getOutlineRatio() const { return _outlineRatio; }
|
||||
|
||||
/// 1.0 is default number of samples
|
||||
void setSampleDensity(float sd) { _sampleDensity = sd; }
|
||||
float getSampleDensity() const { return _sampleDensity; }
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<Bevel> _bevel;
|
||||
|
||||
float _widthRatio;
|
||||
float _thicknessRatio;
|
||||
float _outlineRatio;
|
||||
float _sampleDensity;
|
||||
};
|
||||
|
||||
class Layout : public osg::Object
|
||||
{
|
||||
public:
|
||||
|
||||
Layout();
|
||||
Layout(const Layout& layout, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
META_Object(osgText,Layout)
|
||||
|
||||
/// default Layout implementation used if no other is specified on TextNode
|
||||
static osg::ref_ptr<Layout>& getDefaultLayout();
|
||||
|
||||
virtual void layout(TextNode& text) const;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
class TextTechnique : public osg::Object
|
||||
{
|
||||
public:
|
||||
|
||||
TextTechnique();
|
||||
TextTechnique(const TextTechnique& technique, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
META_Object(osgText, TextTechnique)
|
||||
|
||||
TextNode* getTextNode() { return _textNode; }
|
||||
const TextNode* getTextNode() const { return _textNode; }
|
||||
|
||||
/// default TextTechnique implementation used if no other is specified on TextNode
|
||||
static osg::ref_ptr<TextTechnique>& getDefaultTextTechinque();
|
||||
|
||||
/// start building a new charater layout
|
||||
virtual void start();
|
||||
|
||||
/// called by Layout engine to place individual characters
|
||||
virtual void addCharacter(const osg::Vec3& position, const osg::Vec3& size, Glyph* glyph, Style* style);
|
||||
|
||||
/// called by Layout engine to place individual characters
|
||||
virtual void addCharacter(const osg::Vec3& position, const osg::Vec3& size, Glyph3D* glyph, Style* style);
|
||||
|
||||
/// finish building new charater layout
|
||||
virtual void finish();
|
||||
|
||||
/// provide traversal control
|
||||
virtual void traverse(osg::NodeVisitor& nv);
|
||||
|
||||
protected:
|
||||
|
||||
friend class TextNode;
|
||||
|
||||
void setTextNode(TextNode* textNode) { _textNode = textNode; }
|
||||
|
||||
TextNode* _textNode;
|
||||
};
|
||||
|
||||
class TextNode : public osg::Group
|
||||
{
|
||||
public:
|
||||
|
||||
TextNode();
|
||||
TextNode(const TextNode& text, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
META_Node(osgText, TextNode)
|
||||
|
||||
virtual void traverse(osg::NodeVisitor& nv);
|
||||
|
||||
void setFont(Font* font) { _font = font; }
|
||||
Font* getFont() { return _font.get(); }
|
||||
const Font* getFont() const { return _font.get(); }
|
||||
Font* getActiveFont() { return _font.valid() ? _font.get() : Font::getDefaultFont().get(); }
|
||||
const Font* getActiveFont() const { return _font.valid() ? _font.get() : Font::getDefaultFont().get(); }
|
||||
|
||||
void setStyle(Style* style) { _style = style; }
|
||||
Style* getStyle() { return _style.get(); }
|
||||
const Style* getStyle() const { return _style.get(); }
|
||||
Style* getActiveStyle() { return _style.valid() ? _style.get() : Style::getDefaultStyle().get(); }
|
||||
const Style* getActiveStyle() const { return _style.valid() ? _style.get() : Style::getDefaultStyle().get(); }
|
||||
|
||||
void setLayout(Layout* layout) { _layout = layout; }
|
||||
Layout* getLayout() { return _layout.get(); }
|
||||
const Layout* getLayout() const { return _layout.get(); }
|
||||
const Layout* getActiveLayout() const { return _layout.valid() ? _layout.get() : Layout::getDefaultLayout().get(); }
|
||||
|
||||
void setTextTechnique(TextTechnique* technique);
|
||||
TextTechnique* getTextTechnique() { return _technique.get(); }
|
||||
const TextTechnique* getTextTechnique() const { return _technique.get(); }
|
||||
|
||||
void setText(const std::string& str);
|
||||
void setText(const String& str) { _string = str; }
|
||||
String& getText() { return _string; }
|
||||
const String& getText() const { return _string; }
|
||||
|
||||
void setPosition(const osg::Vec3d& position) { _position = position; }
|
||||
const osg::Vec3d& getPosition() const { return _position; }
|
||||
|
||||
void setRotation(const osg::Quat& rotation) { _rotation = rotation; }
|
||||
const osg::Quat& getRotation() const { return _rotation; }
|
||||
|
||||
void setCharacterSize(float characterSize) { _characterSize = characterSize; }
|
||||
float getCharacterSize() const { return _characterSize; }
|
||||
|
||||
/// force a regeneration of the rendering backend required to represent the text.
|
||||
virtual void update();
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~TextNode();
|
||||
|
||||
osg::ref_ptr<Font> _font;
|
||||
osg::ref_ptr<Style> _style;
|
||||
osg::ref_ptr<Layout> _layout;
|
||||
osg::ref_ptr<TextTechnique> _technique;
|
||||
|
||||
String _string;
|
||||
osg::Vec3d _position;
|
||||
osg::Quat _rotation;
|
||||
float _characterSize;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,10 +17,9 @@
|
||||
#include <freetype/ftoutln.h>
|
||||
#include <freetype/ftbbox.h>
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/Notify>
|
||||
#include <osg/io_utils>
|
||||
#include <osgDB/WriteFile>
|
||||
#include <osgUtil/SmoothingVisitor>
|
||||
#include <osgUtil/Tessellator>
|
||||
|
||||
namespace FreeType
|
||||
{
|
||||
@@ -30,7 +29,6 @@ struct Char3DInfo
|
||||
Char3DInfo(int numSteps):
|
||||
_verts( new osg::Vec3Array ),
|
||||
_geometry( new osg::Geometry ),
|
||||
_idx(0),
|
||||
_numSteps(numSteps),
|
||||
_maxY(-FLT_MAX),
|
||||
_maxX(-FLT_MAX),
|
||||
@@ -38,21 +36,26 @@ struct Char3DInfo
|
||||
_minY(FLT_MAX),
|
||||
_coord_scale(1.0/64.0)
|
||||
{
|
||||
_geometry->setVertexArray(_verts.get());
|
||||
}
|
||||
|
||||
~Char3DInfo()
|
||||
{
|
||||
}
|
||||
|
||||
void completeCurrentPrimitiveSet()
|
||||
{
|
||||
if (_currentPrimitiveSet.valid() && _currentPrimitiveSet->size()>1)
|
||||
{
|
||||
_geometry->addPrimitiveSet( _currentPrimitiveSet.get() );
|
||||
}
|
||||
_currentPrimitiveSet = 0;
|
||||
}
|
||||
|
||||
osg::Geometry* get()
|
||||
{
|
||||
int len = _verts->size()-_idx;
|
||||
if (len)
|
||||
{
|
||||
_geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, _idx, len ) );
|
||||
_idx = _verts->size();
|
||||
}
|
||||
completeCurrentPrimitiveSet();
|
||||
|
||||
_geometry->setVertexArray(_verts.get());
|
||||
return _geometry.get();
|
||||
}
|
||||
|
||||
@@ -68,18 +71,32 @@ struct Char3DInfo
|
||||
return;
|
||||
}
|
||||
|
||||
_verts->push_back( pos );
|
||||
setMinMax(pos);
|
||||
if (!_currentPrimitiveSet)
|
||||
{
|
||||
_currentPrimitiveSet = new osg::DrawElementsUShort( osg::PrimitiveSet::POLYGON);
|
||||
_currentPrimitiveSet->setName("boundary");
|
||||
}
|
||||
|
||||
if (!(_currentPrimitiveSet->empty()) &&
|
||||
(*_verts)[(*_currentPrimitiveSet)[0]] == pos)
|
||||
{
|
||||
OSG_NOTICE<<"Repeating first index "<<(*_currentPrimitiveSet)[0]<<", "<<(*_verts)[(*_currentPrimitiveSet)[0]]<<", pos="<<pos<<std::endl;
|
||||
_currentPrimitiveSet->push_back( (*_currentPrimitiveSet)[0] );
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentPrimitiveSet->push_back( _verts->size() );
|
||||
|
||||
_verts->push_back( pos );
|
||||
|
||||
setMinMax(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void moveTo(const osg::Vec2& pos)
|
||||
{
|
||||
if (_verts->size())
|
||||
{
|
||||
int len = _verts->size()-_idx;
|
||||
_geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, _idx, len ) );
|
||||
}
|
||||
_idx = _verts->size();
|
||||
completeCurrentPrimitiveSet();
|
||||
|
||||
addVertex( osg::Vec3(pos.x(),pos.y(),0) );
|
||||
|
||||
}
|
||||
@@ -87,6 +104,7 @@ struct Char3DInfo
|
||||
{
|
||||
addVertex( osg::Vec3(pos.x(),pos.y(),0) );
|
||||
}
|
||||
|
||||
void conicTo(const osg::Vec2& control, const osg::Vec2& pos)
|
||||
{
|
||||
osg::Vec3 p0 = _previous;
|
||||
@@ -140,9 +158,9 @@ struct Char3DInfo
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> _verts;
|
||||
osg::ref_ptr<osg::DrawElementsUShort> _currentPrimitiveSet;
|
||||
osg::ref_ptr<osg::Geometry> _geometry;
|
||||
osg::Vec3 _previous;
|
||||
int _idx;
|
||||
int _numSteps;
|
||||
double _maxY;
|
||||
double _maxX;
|
||||
@@ -372,7 +390,7 @@ osgText::Glyph* FreeTypeFont::getGlyph(const osgText::FontResolution& fontRes, u
|
||||
unsigned int width = sourceWidth;
|
||||
unsigned int height = sourceHeight;
|
||||
|
||||
osg::ref_ptr<osgText::Glyph> glyph = new osgText::Glyph(charcode);
|
||||
osg::ref_ptr<osgText::Glyph> glyph = new osgText::Glyph(_facade, charcode);
|
||||
|
||||
unsigned int dataSize = width*height;
|
||||
unsigned char* data = new unsigned char[dataSize];
|
||||
@@ -423,7 +441,11 @@ osgText::Glyph* FreeTypeFont::getGlyph(const osgText::FontResolution& fontRes, u
|
||||
|
||||
FT_Glyph_Metrics* metrics = &(_face->glyph->metrics);
|
||||
|
||||
#if 0
|
||||
float coord_scale = _freetype_scale/64.0f;
|
||||
#else
|
||||
float coord_scale = 1.0f/64.0f;
|
||||
#endif
|
||||
|
||||
glyph->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX * coord_scale,(float)(metrics->horiBearingY-metrics->height) * coord_scale)); // bottom left.
|
||||
glyph->setHorizontalAdvance((float)metrics->horiAdvance * coord_scale);
|
||||
@@ -502,116 +524,13 @@ osgText::Glyph3D * FreeTypeFont::getGlyph3D(unsigned int charcode)
|
||||
rawPrimitives.push_back(dynamic_cast<osg::PrimitiveSet*>((*itr)->clone(osg::CopyOp::DEEP_COPY_ALL)));
|
||||
}
|
||||
|
||||
frontGeo->setVertexArray(char3d.get()->getVertexArray());
|
||||
frontGeo->setPrimitiveSetList(char3d.get()->getPrimitiveSetList());
|
||||
|
||||
osg::ref_ptr<osg::Geometry> wallGeo(new osg::Geometry);
|
||||
wallGeo->setVertexArray(frontGeo->getVertexArray());
|
||||
|
||||
osg::ref_ptr<osg::Geometry> backGeo(new osg::Geometry);
|
||||
backGeo->setVertexArray(frontGeo->getVertexArray());
|
||||
|
||||
|
||||
|
||||
// ** for convenience.
|
||||
osg::Vec3Array * vertices = char3d._verts.get();
|
||||
|
||||
|
||||
|
||||
// ** duplicate the vertex for the back face
|
||||
// ** with a depth equal to the font depth
|
||||
std::size_t len = vertices->size();
|
||||
std::size_t dlen = len * 2;
|
||||
|
||||
vertices->reserve(dlen);
|
||||
|
||||
osg::Vec3Array::iterator begin = vertices->begin();
|
||||
osg::Vec3Array::iterator it = vertices->begin();
|
||||
|
||||
for (std::size_t i = 0; i != len; ++i, ++it)
|
||||
vertices->push_back(*it);
|
||||
// std::copy(begin, begin + len, begin + len + 1); TOCHECK
|
||||
|
||||
|
||||
// ** and decal new vertices
|
||||
unsigned int depth = _facade->getFontDepth();
|
||||
for (std::size_t i = len; i != dlen; ++i)
|
||||
{
|
||||
(*vertices)[i].z() -= depth;
|
||||
}
|
||||
|
||||
osg::Vec3Array::iterator end;
|
||||
|
||||
// ** create wall and back face from the front polygon
|
||||
// ** then accumulate them in the appropriate geometry wallGeo and backGeo
|
||||
for (std::size_t i=0; i < frontGeo->getNumPrimitiveSets(); ++i)
|
||||
{
|
||||
// ** get the front polygon
|
||||
osg::ref_ptr<osg::DrawArrays> daFront(dynamic_cast<osg::DrawArrays*>(frontGeo->getPrimitiveSet(i)));
|
||||
unsigned int idx = daFront->getFirst();
|
||||
unsigned int cnt = daFront->getCount();
|
||||
|
||||
// ** reverse vertices to draw the front face in the CCW
|
||||
std::reverse(begin + idx, begin + idx + cnt);
|
||||
|
||||
// ** create the back polygon
|
||||
osg::ref_ptr<osg::DrawArrays> daBack(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, idx + len, cnt));
|
||||
backGeo->addPrimitiveSet(daBack.get());
|
||||
|
||||
|
||||
// ** create the wall triangle strip
|
||||
osg::ref_ptr<osg::DrawElementsUInt> deWall(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP));
|
||||
wallGeo->addPrimitiveSet(deWall.get());
|
||||
|
||||
// ** link triangle strip
|
||||
deWall->push_back(idx + len);
|
||||
for (unsigned int j = 1; j < cnt; ++j)
|
||||
{
|
||||
deWall->push_back(idx + cnt - j);
|
||||
deWall->push_back(idx + len + j);
|
||||
}
|
||||
deWall->push_back(idx);
|
||||
deWall->push_back(idx + len);
|
||||
deWall->push_back(idx + cnt - 1);
|
||||
}
|
||||
|
||||
// ** tesselate front and back face
|
||||
{
|
||||
osgUtil::Tessellator ts;
|
||||
ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
|
||||
ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
|
||||
ts.retessellatePolygons(*frontGeo);
|
||||
}
|
||||
|
||||
{
|
||||
osgUtil::Tessellator ts;
|
||||
ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE);
|
||||
ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
|
||||
ts.retessellatePolygons(*backGeo);
|
||||
}
|
||||
|
||||
// ** generate normal
|
||||
{
|
||||
osgUtil::SmoothingVisitor sm;
|
||||
osg::ref_ptr<osg::Geode> geode(new osg::Geode);
|
||||
geode->addDrawable(wallGeo.get());
|
||||
geode->accept(sm);
|
||||
}
|
||||
|
||||
// ** save vertices and PrimitiveSetList of each face in the Glyph3D PrimitiveSet face list
|
||||
osg::ref_ptr<osgText::Glyph3D> glyph3D = new osgText::Glyph3D(charcode);
|
||||
osg::ref_ptr<osgText::Glyph3D> glyph3D = new osgText::Glyph3D(_facade, charcode);
|
||||
|
||||
// copy the raw primitive set list before we tessellate it.
|
||||
glyph3D->getRawFacePrimitiveSetList() = rawPrimitives;
|
||||
glyph3D->setRawVertexArray(rawVertices.get());
|
||||
|
||||
glyph3D->setVertexArray(dynamic_cast<osg::Vec3Array*>(frontGeo->getVertexArray()));
|
||||
glyph3D->setNormalArray(dynamic_cast<osg::Vec3Array*>(wallGeo->getNormalArray()));
|
||||
|
||||
glyph3D->getFrontPrimitiveSetList() = frontGeo->getPrimitiveSetList();
|
||||
glyph3D->getWallPrimitiveSetList() = wallGeo->getPrimitiveSetList();
|
||||
glyph3D->getBackPrimitiveSetList() = backGeo->getPrimitiveSetList();
|
||||
|
||||
|
||||
FT_Glyph_Metrics* metrics = &(_face->glyph->metrics);
|
||||
|
||||
@@ -635,6 +554,9 @@ osgText::Glyph3D * FreeTypeFont::getGlyph3D(unsigned int charcode)
|
||||
|
||||
glyph3D->setBoundingBox(bb);
|
||||
|
||||
glyph3D->computeText3DGeometryData();
|
||||
|
||||
|
||||
return glyph3D.release();
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ TXFFont::loadFont(std::istream& stream)
|
||||
continue;
|
||||
|
||||
// add the characters ...
|
||||
osgText::Glyph* glyph = new osgText::Glyph(glyphs[i].ch);
|
||||
osgText::Glyph* glyph = new osgText::Glyph(_facade, glyphs[i].ch);
|
||||
|
||||
unsigned sourceWidth = glyphs[i].width;
|
||||
unsigned sourceHeight = glyphs[i].height;
|
||||
@@ -269,7 +269,7 @@ TXFFont::loadFont(std::istream& stream)
|
||||
}
|
||||
|
||||
// insert a trivial blank character
|
||||
osgText::Glyph* glyph = new osgText::Glyph(' ');
|
||||
osgText::Glyph* glyph = new osgText::Glyph(_facade, ' ');
|
||||
|
||||
unsigned width = 1;
|
||||
unsigned height = 1;
|
||||
|
||||
@@ -83,7 +83,7 @@ QFontImplementation::getGlyph(const osgText::FontResolution& fontRes, unsigned i
|
||||
painter.end();
|
||||
|
||||
// Transfer the rendered image to osg
|
||||
osg::ref_ptr<osgText::Glyph> glyph = new osgText::Glyph(charcode);
|
||||
osg::ref_ptr<osgText::Glyph> glyph = new osgText::Glyph(_facade, charcode);
|
||||
|
||||
unsigned int dataSize = imageWidth*imageHeight;
|
||||
unsigned char* data = new unsigned char[dataSize];
|
||||
|
||||
@@ -11,12 +11,14 @@ SET(LIB_PUBLIC_HEADERS
|
||||
${HEADER_PATH}/Export
|
||||
${HEADER_PATH}/Font
|
||||
${HEADER_PATH}/Font3D
|
||||
${HEADER_PATH}/FadeText
|
||||
${HEADER_PATH}/Glyph
|
||||
${HEADER_PATH}/KerningType
|
||||
${HEADER_PATH}/String
|
||||
${HEADER_PATH}/TextBase
|
||||
${HEADER_PATH}/Text
|
||||
${HEADER_PATH}/TextNode
|
||||
${HEADER_PATH}/Text3D
|
||||
${HEADER_PATH}/FadeText
|
||||
${HEADER_PATH}/Version
|
||||
)
|
||||
|
||||
@@ -26,11 +28,15 @@ ADD_LIBRARY(${LIB_NAME}
|
||||
${LIB_PUBLIC_HEADERS}
|
||||
DefaultFont.cpp
|
||||
DefaultFont.h
|
||||
GlyphGeometry.h
|
||||
GlyphGeometry.cpp
|
||||
Font.cpp
|
||||
String.cpp
|
||||
FadeText.cpp
|
||||
Glyph.cpp
|
||||
String.cpp
|
||||
TextBase.cpp
|
||||
Text.cpp
|
||||
TextNode.cpp
|
||||
Text3D.cpp
|
||||
Version.cpp
|
||||
${OPENSCENEGRAPH_VERSIONINFO_RC}
|
||||
|
||||
@@ -194,7 +194,7 @@ void DefaultFont::constructGlyphs()
|
||||
// populate the glyph mp
|
||||
for(unsigned int i=32;i<127;i++)
|
||||
{
|
||||
osg::ref_ptr<Glyph> glyph = new Glyph(i);
|
||||
osg::ref_ptr<Glyph> glyph = new Glyph(this, i);
|
||||
|
||||
unsigned int dataSize = sourceWidth*sourceHeight;
|
||||
unsigned char* data = new unsigned char[dataSize];
|
||||
|
||||
@@ -493,463 +493,3 @@ void Font::addGlyph(const FontResolution& fontRes, unsigned int charcode, Glyph*
|
||||
glyphTexture->addGlyph(glyph,posX,posY);
|
||||
|
||||
}
|
||||
|
||||
|
||||
GlyphTexture::GlyphTexture():
|
||||
_margin(1),
|
||||
_marginRatio(0.02f),
|
||||
_usedY(0),
|
||||
_partUsedX(0),
|
||||
_partUsedY(0)
|
||||
{
|
||||
setWrap(WRAP_S, CLAMP_TO_EDGE);
|
||||
setWrap(WRAP_T, CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
GlyphTexture::~GlyphTexture()
|
||||
{
|
||||
}
|
||||
|
||||
// return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.
|
||||
int GlyphTexture::compare(const osg::StateAttribute& rhs) const
|
||||
{
|
||||
if (this<&rhs) return -1;
|
||||
else if (this>&rhs) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
|
||||
{
|
||||
int maxAxis = std::max(glyph->s(), glyph->t());
|
||||
int margin = _margin + (int)((float)maxAxis * _marginRatio);
|
||||
|
||||
int width = glyph->s()+2*margin;
|
||||
int height = glyph->t()+2*margin;
|
||||
|
||||
// first check box (_partUsedX,_usedY) to (width,height)
|
||||
if (width <= (getTextureWidth()-_partUsedX) &&
|
||||
height <= (getTextureHeight()-_usedY))
|
||||
{
|
||||
// can fit in existing row.
|
||||
|
||||
// record the position in which the texture will be stored.
|
||||
posX = _partUsedX+margin;
|
||||
posY = _usedY+margin;
|
||||
|
||||
// move used markers on.
|
||||
_partUsedX += width;
|
||||
if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// start an new row.
|
||||
if (width <= getTextureWidth() &&
|
||||
height <= (getTextureHeight()-_partUsedY))
|
||||
{
|
||||
// can fit next row.
|
||||
_partUsedX = 0;
|
||||
_usedY = _partUsedY;
|
||||
|
||||
posX = _partUsedX+margin;
|
||||
posY = _usedY+margin;
|
||||
|
||||
// move used markers on.
|
||||
_partUsedX += width;
|
||||
if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// doesn't fit into glyph.
|
||||
return false;
|
||||
}
|
||||
|
||||
void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
_glyphs.push_back(glyph);
|
||||
for(unsigned int i=0;i<_glyphsToSubload.size();++i)
|
||||
{
|
||||
_glyphsToSubload[i].push_back(glyph);
|
||||
}
|
||||
|
||||
// set up the details of where to place glyph's image in the texture.
|
||||
glyph->setTexture(this);
|
||||
glyph->setTexturePosition(posX,posY);
|
||||
|
||||
glyph->setMinTexCoord( osg::Vec2( static_cast<float>(posX)/static_cast<float>(getTextureWidth()),
|
||||
static_cast<float>(posY)/static_cast<float>(getTextureHeight()) ) );
|
||||
glyph->setMaxTexCoord( osg::Vec2( static_cast<float>(posX+glyph->s())/static_cast<float>(getTextureWidth()),
|
||||
static_cast<float>(posY+glyph->t())/static_cast<float>(getTextureHeight()) ) );
|
||||
}
|
||||
|
||||
void GlyphTexture::apply(osg::State& state) const
|
||||
{
|
||||
// get the contextID (user defined ID of 0 upwards) for the
|
||||
// current OpenGL context.
|
||||
const unsigned int contextID = state.getContextID();
|
||||
|
||||
if (contextID>=_glyphsToSubload.size())
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
// graphics context is beyond the number of glyphsToSubloads, so
|
||||
// we must now copy the glyph list across, this is a potential
|
||||
// threading issue though is multiple applies are happening the
|
||||
// same time on this object - to avoid this condition number of
|
||||
// graphics contexts should be set before create text.
|
||||
for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i)
|
||||
{
|
||||
GlyphPtrList& glyphPtrs = _glyphsToSubload[i];
|
||||
for(GlyphRefList::const_iterator itr=_glyphs.begin();
|
||||
itr!=_glyphs.end();
|
||||
++itr)
|
||||
{
|
||||
glyphPtrs.push_back(itr->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Extensions* extensions = getExtensions(contextID,true);
|
||||
bool generateMipMapSupported = extensions->isGenerateMipMapSupported();
|
||||
|
||||
// get the texture object for the current contextID.
|
||||
TextureObject* textureObject = getTextureObject(contextID);
|
||||
|
||||
bool newTextureObject = (textureObject == 0);
|
||||
|
||||
if (newTextureObject)
|
||||
{
|
||||
GLint maxTextureSize = 256;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
if (maxTextureSize < getTextureWidth() || maxTextureSize < getTextureHeight())
|
||||
{
|
||||
OSG_WARN<<"Warning: osgText::Font texture size of ("<<getTextureWidth()<<", "<<getTextureHeight()<<") too large, unable to create font texture."<<std::endl;
|
||||
OSG_WARN<<" Maximum supported by hardward by native OpenGL implementation is ("<<maxTextureSize<<","<<maxTextureSize<<")."<<std::endl;
|
||||
OSG_WARN<<" Please set OSG_MAX_TEXTURE_SIZE lenvironment variable to "<<maxTextureSize<<" and re-run application."<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// being bound for the first time, need to allocate the texture
|
||||
|
||||
_textureObjectBuffer[contextID] = textureObject = osg::Texture::generateTextureObject(
|
||||
this, contextID,GL_TEXTURE_2D,1,GL_ALPHA,getTextureWidth(), getTextureHeight(),1,0);
|
||||
|
||||
textureObject->bind();
|
||||
|
||||
|
||||
applyTexParameters(GL_TEXTURE_2D,state);
|
||||
|
||||
|
||||
// need to look at generate mip map extension if mip mapping required.
|
||||
switch(_min_filter)
|
||||
{
|
||||
case NEAREST_MIPMAP_NEAREST:
|
||||
case NEAREST_MIPMAP_LINEAR:
|
||||
case LINEAR_MIPMAP_NEAREST:
|
||||
case LINEAR_MIPMAP_LINEAR:
|
||||
if (generateMipMapSupported)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
|
||||
}
|
||||
else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR);
|
||||
break;
|
||||
default:
|
||||
// not mip mapping so no problems.
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int imageDataSize = getTextureHeight()*getTextureWidth();
|
||||
unsigned char* imageData = new unsigned char[imageDataSize];
|
||||
for(unsigned int i=0; i<imageDataSize; ++i)
|
||||
{
|
||||
imageData[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
// OSG_NOTICE<<"Texture width = "<<getTextureWidth()<<std::endl;
|
||||
// OSG_NOTICE<<"Texture height = "<<getTextureHeight()<<std::endl;
|
||||
|
||||
// allocate the texture memory.
|
||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA,
|
||||
getTextureWidth(), getTextureHeight(), 0,
|
||||
GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
imageData );
|
||||
|
||||
delete [] imageData;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// reuse texture by binding.
|
||||
textureObject->bind();
|
||||
|
||||
if (getTextureParameterDirty(contextID))
|
||||
{
|
||||
applyTexParameters(GL_TEXTURE_2D,state);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static const GLubyte* s_renderer = 0;
|
||||
static bool s_subloadAllGlyphsTogether = false;
|
||||
if (!s_renderer)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
s_renderer = glGetString(GL_RENDERER);
|
||||
OSG_INFO<<"glGetString(GL_RENDERER)=="<<s_renderer<<std::endl;
|
||||
if (s_renderer && strstr((const char*)s_renderer,"IMPACT")!=0)
|
||||
{
|
||||
// we're running on an Octane, so need to work around its
|
||||
// subloading bugs by loading all at once.
|
||||
s_subloadAllGlyphsTogether = true;
|
||||
}
|
||||
|
||||
if (s_renderer &&
|
||||
((strstr((const char*)s_renderer,"Radeon")!=0) ||
|
||||
(strstr((const char*)s_renderer,"RADEON")!=0) ||
|
||||
(strstr((const char*)s_renderer,"ALL-IN-WONDER")!=0)))
|
||||
{
|
||||
// we're running on an ATI, so need to work around its
|
||||
// subloading bugs by loading all at once.
|
||||
s_subloadAllGlyphsTogether = true;
|
||||
}
|
||||
|
||||
if (s_renderer && strstr((const char*)s_renderer,"Sun")!=0)
|
||||
{
|
||||
// we're running on an solaris x server, so need to work around its
|
||||
// subloading bugs by loading all at once.
|
||||
s_subloadAllGlyphsTogether = true;
|
||||
}
|
||||
|
||||
const char* str = getenv("OSG_TEXT_INCREMENTAL_SUBLOADING");
|
||||
if (str)
|
||||
{
|
||||
s_subloadAllGlyphsTogether = strcmp(str,"OFF")==0 || strcmp(str,"Off")==0 || strcmp(str,"off")==0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now subload the glyphs that are outstanding for this graphics context.
|
||||
GlyphPtrList& glyphsWereSubloading = _glyphsToSubload[contextID];
|
||||
|
||||
if (!glyphsWereSubloading.empty() || newTextureObject)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
|
||||
if (!s_subloadAllGlyphsTogether)
|
||||
{
|
||||
if (newTextureObject)
|
||||
{
|
||||
for(GlyphRefList::const_iterator itr=_glyphs.begin();
|
||||
itr!=_glyphs.end();
|
||||
++itr)
|
||||
{
|
||||
(*itr)->subload();
|
||||
}
|
||||
}
|
||||
else // just subload the new entries.
|
||||
{
|
||||
// default way of subloading as required.
|
||||
//std::cout<<"subloading"<<std::endl;
|
||||
for(GlyphPtrList::iterator itr=glyphsWereSubloading.begin();
|
||||
itr!=glyphsWereSubloading.end();
|
||||
++itr)
|
||||
{
|
||||
(*itr)->subload();
|
||||
}
|
||||
}
|
||||
|
||||
// clear the list since we have now subloaded them.
|
||||
glyphsWereSubloading.clear();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_INFO<<"osgText::Font loading all glyphs as a single subload."<<std::endl;
|
||||
|
||||
// Octane has bugs in OGL driver which mean that subloads smaller
|
||||
// than 32x32 produce errors, and also cannot handle general alignment,
|
||||
// so to get round this copy all glyphs into a temporary image and
|
||||
// then subload the whole lot in one go.
|
||||
|
||||
int tsize = getTextureHeight() * getTextureWidth();
|
||||
unsigned char *local_data = new unsigned char[tsize];
|
||||
memset( local_data, 0L, tsize);
|
||||
|
||||
for(GlyphRefList::const_iterator itr=_glyphs.begin();
|
||||
itr!=_glyphs.end();
|
||||
++itr)
|
||||
{
|
||||
//(*itr)->subload();
|
||||
|
||||
// Rather than subloading to graphics, we'll write the values
|
||||
// of the glyphs into some intermediate data and subload the
|
||||
// whole thing at the end
|
||||
for( int t = 0; t < (*itr)->t(); t++ )
|
||||
{
|
||||
for( int s = 0; s < (*itr)->s(); s++ )
|
||||
{
|
||||
int sindex = (t*(*itr)->s()+s);
|
||||
int dindex =
|
||||
((((*itr)->getTexturePositionY()+t) * getTextureWidth()) +
|
||||
((*itr)->getTexturePositionX()+s));
|
||||
|
||||
const unsigned char *sptr = &(*itr)->data()[sindex];
|
||||
unsigned char *dptr = &local_data[dindex];
|
||||
|
||||
(*dptr) = (*sptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear the list since we have now subloaded them.
|
||||
glyphsWereSubloading.clear();
|
||||
|
||||
// Subload the image once
|
||||
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
|
||||
getTextureWidth(),
|
||||
getTextureHeight(),
|
||||
GL_ALPHA, GL_UNSIGNED_BYTE, local_data );
|
||||
|
||||
delete [] local_data;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// OSG_INFO << "no need to subload "<<std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if (generateMipMapTurnedOn)
|
||||
// {
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GlyphTexture::setThreadSafeRefUnref(bool threadSafe)
|
||||
{
|
||||
osg::Texture2D::setThreadSafeRefUnref(threadSafe);
|
||||
}
|
||||
|
||||
void GlyphTexture::resizeGLObjectBuffers(unsigned int maxSize)
|
||||
{
|
||||
osg::Texture2D::resizeGLObjectBuffers(maxSize);
|
||||
_glyphsToSubload.resize(maxSize);
|
||||
}
|
||||
|
||||
|
||||
// all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK.
|
||||
Glyph::Glyph(unsigned int glyphCode):
|
||||
_font(0),
|
||||
_glyphCode(glyphCode),
|
||||
_horizontalBearing(0.0f,0.f),
|
||||
_horizontalAdvance(0.f),
|
||||
_verticalBearing(0.0f,0.f),
|
||||
_verticalAdvance(0.f),
|
||||
_texture(0),
|
||||
_texturePosX(0),
|
||||
_texturePosY(0),
|
||||
_minTexCoord(0.0f,0.0f),
|
||||
_maxTexCoord(0.0f,0.0f)
|
||||
{
|
||||
setThreadSafeRefUnref(true);
|
||||
}
|
||||
|
||||
Glyph::~Glyph()
|
||||
{
|
||||
}
|
||||
|
||||
void Glyph::setHorizontalBearing(const osg::Vec2& bearing) { _horizontalBearing=bearing; }
|
||||
const osg::Vec2& Glyph::getHorizontalBearing() const { return _horizontalBearing; }
|
||||
|
||||
void Glyph::setHorizontalAdvance(float advance) { _horizontalAdvance=advance; }
|
||||
float Glyph::getHorizontalAdvance() const { return _horizontalAdvance; }
|
||||
|
||||
void Glyph::setVerticalBearing(const osg::Vec2& bearing) { _verticalBearing=bearing; }
|
||||
const osg::Vec2& Glyph::getVerticalBearing() const { return _verticalBearing; }
|
||||
|
||||
void Glyph::setVerticalAdvance(float advance) { _verticalAdvance=advance; }
|
||||
float Glyph::getVerticalAdvance() const { return _verticalAdvance; }
|
||||
|
||||
void Glyph::setTexture(GlyphTexture* texture) { _texture = texture; }
|
||||
GlyphTexture* Glyph::getTexture() { return _texture; }
|
||||
const GlyphTexture* Glyph::getTexture() const { return _texture; }
|
||||
|
||||
void Glyph::setTexturePosition(int posX,int posY) { _texturePosX = posX; _texturePosY = posY; }
|
||||
int Glyph::getTexturePositionX() const { return _texturePosX; }
|
||||
int Glyph::getTexturePositionY() const { return _texturePosY; }
|
||||
|
||||
void Glyph::setMinTexCoord(const osg::Vec2& coord) { _minTexCoord=coord; }
|
||||
const osg::Vec2& Glyph::getMinTexCoord() const { return _minTexCoord; }
|
||||
|
||||
void Glyph::setMaxTexCoord(const osg::Vec2& coord) { _maxTexCoord=coord; }
|
||||
const osg::Vec2& Glyph::getMaxTexCoord() const { return _maxTexCoord; }
|
||||
|
||||
void Glyph::subload() const
|
||||
{
|
||||
GLenum errorNo = glGetError();
|
||||
if (errorNo!=GL_NO_ERROR)
|
||||
{
|
||||
#ifdef OSG_GLU_AVAILABLE
|
||||
const GLubyte* msg = gluErrorString(errorNo);
|
||||
if (msg) { OSG_WARN<<"before Glyph::subload(): detected OpenGL error: "<<msg<<std::endl; }
|
||||
else { OSG_WARN<<"before Glyph::subload(): detected OpenGL error number: "<<errorNo<<std::endl; }
|
||||
#else
|
||||
OSG_WARN<<"before Glyph::subload(): detected OpenGL error number: "<<errorNo<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(s() <= 0 || t() <= 0)
|
||||
{
|
||||
OSG_INFO<<"Glyph::subload(): texture sub-image width and/or height of 0, ignoring operation."<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT,getPacking());
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D,0,
|
||||
_texturePosX,_texturePosY,
|
||||
s(),t(),
|
||||
(GLenum)getPixelFormat(),
|
||||
(GLenum)getDataType(),
|
||||
data());
|
||||
|
||||
errorNo = glGetError();
|
||||
if (errorNo!=GL_NO_ERROR)
|
||||
{
|
||||
|
||||
|
||||
#ifdef OSG_GLU_AVAILABLE
|
||||
const GLubyte* msg = gluErrorString(errorNo);
|
||||
if (msg) { OSG_WARN<<"after Glyph::subload() : detected OpenGL error: "<<msg<<std::endl; }
|
||||
else { OSG_WARN<<"after Glyph::subload() : detected OpenGL error number: "<<errorNo<<std::endl; }
|
||||
#else
|
||||
OSG_WARN<<"after Glyph::subload() : detected OpenGL error number: "<<errorNo<<std::endl;
|
||||
#endif
|
||||
|
||||
OSG_WARN<< "\tglTexSubImage2D(0x"<<hex<<GL_TEXTURE_2D<<dec<<" ,"<<0<<"\t"<<std::endl<<
|
||||
"\t "<<_texturePosX<<" ,"<<_texturePosY<<std::endl<<
|
||||
"\t "<<s()<<" ,"<<t()<<std::endl<<hex<<
|
||||
"\t 0x"<<(GLenum)getPixelFormat()<<std::endl<<
|
||||
"\t 0x"<<(GLenum)getDataType()<<std::endl<<
|
||||
"\t 0x"<<(unsigned long)data()<<");"<<dec<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Glyph3D::setThreadSafeRefUnref(bool threadSafe)
|
||||
{
|
||||
if (_vertexArray.valid()) _vertexArray->setThreadSafeRefUnref(threadSafe);
|
||||
if (_normalArray.valid()) _normalArray->setThreadSafeRefUnref(threadSafe);
|
||||
}
|
||||
|
||||
1061
src/osgText/GlyphGeometry.cpp
Normal file
1061
src/osgText/GlyphGeometry.cpp
Normal file
File diff suppressed because it is too large
Load Diff
32
src/osgText/GlyphGeometry.h
Normal file
32
src/osgText/GlyphGeometry.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* -*-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 <osgText/TextNode>
|
||||
|
||||
namespace osgText
|
||||
{
|
||||
|
||||
extern osg::Geometry* computeGlyphGeometry(osgText::Glyph3D* glyph, float bevelThickness, float shellThickness);
|
||||
|
||||
extern osg::Geometry* computeTextGeometry(osgText::Glyph3D* glyph, float width);
|
||||
|
||||
extern osg::Geometry* computeTextGeometry(osg::Geometry* glyphGeometry, const Bevel& profile, float width);
|
||||
|
||||
extern osg::Geometry* computeShellGeometry(osg::Geometry* glyphGeometry, const Bevel& profile, float width);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
425
src/osgText/TextNode.cpp
Normal file
425
src/osgText/TextNode.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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 <osgText/TextNode>
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/Geode>
|
||||
#include <osgUtil/SmoothingVisitor>
|
||||
|
||||
#include <osg/io_utils>
|
||||
|
||||
using namespace osgText;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Bevel
|
||||
//
|
||||
Bevel::Bevel()
|
||||
{
|
||||
_thickness = 0.02f;
|
||||
flatBevel();
|
||||
}
|
||||
|
||||
Bevel::Bevel(const Bevel& bevel, const osg::CopyOp&):
|
||||
_thickness(bevel._thickness),
|
||||
_vertices(bevel._vertices)
|
||||
{
|
||||
}
|
||||
|
||||
void Bevel::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 Bevel::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 Bevel::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 Bevel::print(std::ostream& fout)
|
||||
{
|
||||
OSG_NOTICE<<"print bevel"<<std::endl;
|
||||
for(Vertices::iterator itr = _vertices.begin();
|
||||
itr != _vertices.end();
|
||||
++itr)
|
||||
{
|
||||
OSG_NOTICE<<" "<<*itr<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Style
|
||||
//
|
||||
Style::Style():
|
||||
_widthRatio(1.0f),
|
||||
_thicknessRatio(0.0f),
|
||||
_outlineRatio(0.0f),
|
||||
_sampleDensity(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
Style::Style(const Style& style, const osg::CopyOp& copyop):
|
||||
osg::Object(style,copyop),
|
||||
_bevel(dynamic_cast<Bevel*>(copyop(style._bevel.get()))),
|
||||
_widthRatio(style._widthRatio),
|
||||
_thicknessRatio(style._thicknessRatio),
|
||||
_outlineRatio(style._outlineRatio),
|
||||
_sampleDensity(style._sampleDensity)
|
||||
{
|
||||
}
|
||||
|
||||
/// default Layout implementation used if no other is specified on TextNode
|
||||
osg::ref_ptr<Style>& Style::getDefaultStyle()
|
||||
{
|
||||
static OpenThreads::Mutex s_DefaultStyleMutex;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_DefaultStyleMutex);
|
||||
|
||||
static osg::ref_ptr<Style> s_defaultStyle = new Style;
|
||||
return s_defaultStyle;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Layout
|
||||
//
|
||||
Layout::Layout()
|
||||
{
|
||||
}
|
||||
|
||||
Layout::Layout(const Layout& layout, const osg::CopyOp& copyop):
|
||||
osg::Object(layout,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
osg::ref_ptr<Layout>& Layout::getDefaultLayout()
|
||||
{
|
||||
static OpenThreads::Mutex s_DefaultLayoutMutex;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_DefaultLayoutMutex);
|
||||
|
||||
static osg::ref_ptr<Layout> s_defaultLayout = new Layout;
|
||||
return s_defaultLayout;
|
||||
}
|
||||
|
||||
void Layout::layout(TextNode& text) const
|
||||
{
|
||||
OSG_NOTICE<<"Layout::layout"<<std::endl;
|
||||
|
||||
Font* font = text.getActiveFont();
|
||||
Style* style = text.getActiveStyle();
|
||||
TextTechnique* technique = text.getTextTechnique();
|
||||
const String& str = text.getText();
|
||||
|
||||
if (!text.getTextTechnique())
|
||||
{
|
||||
OSG_NOTICE<<"Warning: no TextTechnique assigned to Layout"<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
osg::Vec3 pos(0.0f,0.0f,0.0f);
|
||||
float characterSize = text.getCharacterSize();
|
||||
osg::Vec3 size(characterSize, characterSize, 0.0);
|
||||
if (style)
|
||||
{
|
||||
size.y() = characterSize;
|
||||
size.z() = characterSize;
|
||||
}
|
||||
|
||||
|
||||
osgText::FontResolution resolution(32,32);
|
||||
if (style)
|
||||
{
|
||||
resolution.first = static_cast<unsigned int>(static_cast<float>(resolution.first)*style->getSampleDensity());
|
||||
resolution.second = static_cast<unsigned int>(static_cast<float>(resolution.second)*style->getSampleDensity());
|
||||
}
|
||||
|
||||
float characterWidthScale = 1.0f;
|
||||
float characterHeightScale = 1.0f;
|
||||
|
||||
bool textIs3D = (style && style->getThicknessRatio()!=0.0);
|
||||
if (textIs3D)
|
||||
{
|
||||
characterWidthScale = font->getScale();
|
||||
characterHeightScale = font->getScale();
|
||||
}
|
||||
else
|
||||
{
|
||||
characterWidthScale = 1.0f/static_cast<float>(resolution.first);
|
||||
characterHeightScale = 1.0f/static_cast<float>(resolution.second);
|
||||
}
|
||||
|
||||
osgText::KerningType kerningType = osgText::KERNING_DEFAULT;
|
||||
|
||||
technique->start();
|
||||
|
||||
unsigned int previousCharcode = 0;
|
||||
for(unsigned int i=0; i<str.size(); ++i)
|
||||
{
|
||||
unsigned int charcode = str[i];
|
||||
|
||||
if (size.z()==0.0f)
|
||||
{
|
||||
osgText::Glyph* glyph = font->getGlyph(resolution, charcode);
|
||||
if (glyph)
|
||||
{
|
||||
technique->addCharacter(pos, size, glyph, style);
|
||||
pos += osg::Vec3(size.x()*(glyph->getHorizontalAdvance()*characterWidthScale), 0.0f ,0.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
osgText::Glyph3D* glyph = font->getGlyph3D(charcode);
|
||||
OSG_NOTICE<<"pos = "<<pos<<", charcode="<<charcode<<", glyph="<<glyph<< std::endl;
|
||||
if (glyph)
|
||||
{
|
||||
osg::Vec3 local_scale( size );
|
||||
local_scale *= (1.0f/font->getScale());
|
||||
|
||||
technique->addCharacter(pos, local_scale, glyph, style);
|
||||
pos += osg::Vec3(size.x()*(glyph->getHorizontalWidth()/font->getScale()), 0.0f ,0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (previousCharcode!=0 && charcode!=0)
|
||||
{
|
||||
osg::Vec2 offset = font->getKerning(previousCharcode, charcode, kerningType);
|
||||
OSG_NOTICE<<" offset = "<<offset<< std::endl;
|
||||
pos.x() += offset.x();
|
||||
pos.y() += offset.y();
|
||||
}
|
||||
|
||||
previousCharcode = charcode;
|
||||
}
|
||||
|
||||
technique->finish();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TextTechnique
|
||||
//
|
||||
TextTechnique::TextTechnique():
|
||||
_textNode(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
TextTechnique::TextTechnique(const TextTechnique& technique, const osg::CopyOp& copyop):
|
||||
osg::Object(technique, copyop),
|
||||
_textNode(0)
|
||||
{
|
||||
}
|
||||
|
||||
osg::ref_ptr<TextTechnique>& TextTechnique::getDefaultTextTechinque()
|
||||
{
|
||||
static OpenThreads::Mutex s_DefaultTextTechniqueMutex;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_DefaultTextTechniqueMutex);
|
||||
|
||||
static osg::ref_ptr<TextTechnique> s_defaultTextTechnique = new TextTechnique;
|
||||
return s_defaultTextTechnique;
|
||||
}
|
||||
|
||||
void TextTechnique::start()
|
||||
{
|
||||
OSG_NOTICE<<"TextTechnique::start()"<<std::endl;
|
||||
}
|
||||
|
||||
void TextTechnique::addCharacter(const osg::Vec3& position, const osg::Vec3& size, Glyph* glyph, Style* style)
|
||||
{
|
||||
OSG_NOTICE<<"TextTechnique::addCharacter 2D("<<position<<", "<<size<<", "<<glyph<<", "<<style<<")"<<std::endl;
|
||||
}
|
||||
|
||||
void TextTechnique::addCharacter(const osg::Vec3& position, const osg::Vec3& size, Glyph3D* glyph, Style* style)
|
||||
{
|
||||
OSG_NOTICE<<"TextTechnique::addCharacter 3D("<<position<<", "<<size<<", "<<glyph<<", "<<style<<")"<<std::endl;
|
||||
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
|
||||
transform->setPosition(position);
|
||||
transform->setAttitude(osg::Quat(osg::inDegrees(90.0),osg::Vec3d(1.0,0.0,0.0)));
|
||||
transform->setScale(size);
|
||||
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
|
||||
const Bevel* bevel = style ? style->getBevel() : 0;
|
||||
bool outline = style ? style->getOutlineRatio()>0.0f : false;
|
||||
float width = style->getThicknessRatio();
|
||||
float creaseAngle = 30.0f;
|
||||
bool smooth = true;
|
||||
|
||||
if (bevel)
|
||||
{
|
||||
float thickness = bevel->getBevelThickness();
|
||||
|
||||
osg::ref_ptr<osg::Geometry> glyphGeometry = osgText::computeGlyphGeometry(glyph, thickness, width);
|
||||
osg::ref_ptr<osg::Geometry> textGeometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width);
|
||||
osg::ref_ptr<osg::Geometry> shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0;
|
||||
if (textGeometry.valid()) geode->addDrawable(textGeometry.get());
|
||||
if (shellGeometry.valid()) geode->addDrawable(shellGeometry.get());
|
||||
|
||||
// create the normals
|
||||
if (smooth && textGeometry.valid())
|
||||
{
|
||||
osgUtil::SmoothingVisitor::smooth(*textGeometry, osg::DegreesToRadians(creaseAngle));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> textGeometry = osgText::computeTextGeometry(glyph, width);
|
||||
if (textGeometry.valid()) geode->addDrawable(textGeometry.get());
|
||||
|
||||
// create the normals
|
||||
if (smooth && textGeometry.valid())
|
||||
{
|
||||
osgUtil::SmoothingVisitor::smooth(*textGeometry, osg::DegreesToRadians(creaseAngle));
|
||||
}
|
||||
}
|
||||
|
||||
transform->addChild(geode.get());
|
||||
|
||||
_textNode->addChild(transform.get());
|
||||
|
||||
transform->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
|
||||
|
||||
}
|
||||
|
||||
void TextTechnique::finish()
|
||||
{
|
||||
OSG_NOTICE<<"TextTechnique::finish()"<<std::endl;
|
||||
}
|
||||
|
||||
void TextTechnique::traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
// OSG_NOTICE<<"TextTechnique::traverse()"<<std::endl;
|
||||
if (_textNode) _textNode->Group::traverse(nv);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TextNode
|
||||
//
|
||||
TextNode::TextNode():
|
||||
_characterSize(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
TextNode::TextNode(const TextNode& text, const osg::CopyOp& copyop):
|
||||
osg::Group(text, copyop)
|
||||
{
|
||||
}
|
||||
|
||||
TextNode::~TextNode()
|
||||
{
|
||||
setTextTechnique(0);
|
||||
}
|
||||
|
||||
void TextNode::traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
if (_technique.valid())
|
||||
{
|
||||
_technique->traverse(nv);
|
||||
}
|
||||
else
|
||||
{
|
||||
Group::traverse(nv);
|
||||
}
|
||||
}
|
||||
|
||||
void TextNode::setTextTechnique(TextTechnique* technique)
|
||||
{
|
||||
if (_technique==technique) return;
|
||||
|
||||
if (_technique.valid()) _technique->setTextNode(0);
|
||||
|
||||
if (TextTechnique::getDefaultTextTechinque()==technique)
|
||||
{
|
||||
OSG_NOTICE<<"Warning: Attempt to assign DefaultTextTechnique() prototype to TextNode::setTextTechnique(..), assigning a clone() of it instead."<<std::endl;
|
||||
technique = new TextTechnique(*TextTechnique::getDefaultTextTechinque());
|
||||
}
|
||||
|
||||
_technique = technique;
|
||||
|
||||
if (_technique.valid()) _technique->setTextNode(this);
|
||||
}
|
||||
|
||||
|
||||
void TextNode::update()
|
||||
{
|
||||
getActiveLayout()->layout(*this);
|
||||
}
|
||||
|
||||
void TextNode::setText(const std::string& str)
|
||||
{
|
||||
_string.set(str);
|
||||
}
|
||||
Reference in New Issue
Block a user