| 22 | | #include <osgDB/ReadFile> |
| 23 | | |
| 24 | | #include <osgGA/TrackballManipulator> |
| 25 | | #include <osgGA/StateSetManipulator> |
| 26 | | |
| 27 | | #include <osg/Geode> |
| 28 | | #include <osg/Geometry> |
| 29 | | #include <osg/Material> |
| 30 | | #include <osg/Shape> |
| 31 | | #include <osg/ShapeDrawable> |
| 32 | | #include <osgText/Text3D> |
| 33 | | |
| 34 | | #include <iostream> |
| 35 | | #include <sstream> |
| 36 | | |
| 37 | | |
| 38 | | |
| 39 | | // create text which sits in 3D space such as would be inserted into a normal model |
| 40 | | osg::Group* create3DText(const osg::Vec3& center,float radius) |
| 41 | | { |
| 42 | | |
| 43 | | osg::Geode* geode = new osg::Geode; |
| 44 | | |
| 45 | | //////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 46 | | // |
| 47 | | // Examples of how to set up axis/orientation alignments |
| 48 | | // |
| 49 | | |
| 50 | | float characterSize=radius*0.2f; |
| 51 | | float characterDepth=characterSize*0.2f; |
| 52 | | |
| 53 | | osg::Vec3 pos(center.x()-radius*.5f,center.y()-radius*.5f,center.z()-radius*.5f); |
| 54 | | |
| 55 | | osgText::Text3D* text1 = new osgText::Text3D; |
| 56 | | text1->setFont("fonts/arial.ttf"); |
| 57 | | text1->setCharacterSize(characterSize); |
| 58 | | text1->setCharacterDepth(characterDepth); |
| 59 | | text1->setPosition(pos); |
| 60 | | text1->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); |
| 61 | | text1->setAxisAlignment(osgText::Text3D::XY_PLANE); |
| 62 | | text1->setText("XY_PLANE"); |
| 63 | | geode->addDrawable(text1); |
| 64 | | |
| 65 | | osgText::Text3D* text2 = new osgText::Text3D; |
| 66 | | text2->setFont("fonts/times.ttf"); |
| 67 | | text2->setCharacterSize(characterSize); |
| 68 | | text2->setCharacterDepth(characterDepth); |
| 69 | | text2->setPosition(pos); |
| 70 | | text2->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); |
| 71 | | text2->setAxisAlignment(osgText::Text3D::YZ_PLANE); |
| 72 | | text2->setText("YZ_PLANE"); |
| 73 | | geode->addDrawable(text2); |
| 74 | | |
| 75 | | osgText::Text3D* text3 = new osgText::Text3D; |
| 76 | | text3->setFont("fonts/dirtydoz.ttf"); |
| 77 | | text3->setCharacterSize(characterSize); |
| 78 | | text3->setCharacterDepth(characterDepth); |
| 79 | | text3->setPosition(pos); |
| 80 | | text3->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); |
| 81 | | text3->setAxisAlignment(osgText::Text3D::XZ_PLANE); |
| 82 | | text3->setText("XZ_PLANE"); |
| 83 | | geode->addDrawable(text3); |
| 84 | | |
| 85 | | osgText::Text3D* text7 = new osgText::Text3D; |
| 86 | | text7->setFont("fonts/times.ttf"); |
| 87 | | text7->setCharacterSize(characterSize); |
| 88 | | text7->setCharacterDepth(characterSize*0.2f); |
| 89 | | text7->setPosition(center - osg::Vec3(0.0, 0.0, 0.6)); |
| 90 | | text7->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); |
| 91 | | text7->setAxisAlignment(osgText::Text3D::SCREEN); |
| 92 | | text7->setCharacterSizeMode(osgText::Text3D::OBJECT_COORDS); |
| 93 | | text7->setText("CharacterSizeMode OBJECT_COORDS (default)"); |
| 94 | | geode->addDrawable(text7); |
| 95 | | |
| 96 | | osg::ShapeDrawable* shape = new osg::ShapeDrawable(new osg::Sphere(center,characterSize*0.2f)); |
| 97 | | shape->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::ON); |
| 98 | | geode->addDrawable(shape); |
| 99 | | |
| 100 | | osg::Group* rootNode = new osg::Group; |
| 101 | | rootNode->addChild(geode); |
| 102 | | |
| 103 | | osg::Material* front = new osg::Material; |
| 104 | | front->setAlpha(osg::Material::FRONT_AND_BACK,1); |
| 105 | | front->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2,0.2,0.2,1.0)); |
| 106 | | front->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(.0,.0,1.0,1.0)); |
| 107 | | rootNode->getOrCreateStateSet()->setAttributeAndModes(front); |
| 108 | | |
| 109 | | |
| 110 | | return rootNode; |
| 111 | | } |
| 112 | | |
| 113 | | int main(int, char**) |
| 114 | | { |
| 115 | | osgViewer::Viewer viewer; |
| 116 | | |
| 117 | | osg::Vec3 center(0.0f,0.0f,0.0f); |
| 118 | | float radius = 1.0f; |
| 119 | | |
| 120 | | osg::Group* root = new osg::Group; |
| 121 | | root->addChild(create3DText(center, radius)); |
| 122 | | |
| 123 | | viewer.setSceneData(root); |
| 124 | | viewer.setCameraManipulator(new osgGA::TrackballManipulator()); |
| 125 | | viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); |
| 126 | | |
| 127 | | viewer.addEventHandler(new osgViewer::ThreadingHandler); |
| 128 | | viewer.addEventHandler(new osgViewer::WindowSizeHandler); |
| 129 | | viewer.addEventHandler(new osgViewer::StatsHandler); |
| 130 | | |
| 131 | | |
| 132 | | viewer.run(); |
| 133 | | |
| 134 | | return 0; |
| 135 | | } |
| 136 | | |
| 137 | | |
| | 29 | #include <osg/io_utils> |
| | 30 | |
| | 31 | extern int main_orig(int, char**); |
| | 32 | extern int main_test(int, char**); |
| | 33 | |
| | 34 | |
| | 35 | float computeAngle(osg::Vec3& v1, osg::Vec3& v2, osg::Vec3& v3) |
| | 36 | { |
| | 37 | osg::Vec3 v12(v1-v2); |
| | 38 | osg::Vec3 v32(v3-v2); |
| | 39 | v12.normalize(); |
| | 40 | v32.normalize(); |
| | 41 | float dot = v12*v32; |
| | 42 | float angle = acosf(dot); |
| | 43 | OSG_NOTICE<<" v1="<<v1<<", v2="<<v2<<", v3="<<v3<<", dot_angle="<<osg::RadiansToDegrees(angle)<<std::endl; |
| | 44 | return angle; |
| | 45 | } |
| | 46 | |
| | 47 | void computeBoundaryAngles(osg::Vec3Array& vertices, unsigned int start, unsigned int count) |
| | 48 | { |
| | 49 | OSG_NOTICE<<"computeBoundaryAngles("<<vertices.size()<<", "<<start<<", "<<count<<")"<<std::endl; |
| | 50 | if (vertices[start+count-1]==vertices[start]) |
| | 51 | { |
| | 52 | OSG_NOTICE<<"is a line loop"<<std::endl; |
| | 53 | } |
| | 54 | else |
| | 55 | { |
| | 56 | OSG_NOTICE<<"is not line loop, ("<<vertices[start+count-1]<<"), ("<<vertices[start]<<")"<<std::endl; |
| | 57 | return; |
| | 58 | } |
| | 59 | |
| | 60 | computeAngle(vertices[start+count-2],vertices[start],vertices[start+1]); |
| | 61 | for(unsigned int i=start+1; i<start+count-1; ++i) |
| | 62 | { |
| | 63 | computeAngle(vertices[i-1],vertices[i],vertices[i+1]); |
| | 64 | } |
| | 65 | computeAngle(vertices[start+count-2],vertices[start],vertices[start+1]); |
| | 66 | } |
| | 67 | |
| | 68 | void computeBoundaryAngles(osg::Geometry* geometry) |
| | 69 | { |
| | 70 | OSG_NOTICE<<"computeBoundaryAngles("<<geometry<<")"<<std::endl; |
| | 71 | osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()); |
| | 72 | osg::Geometry::PrimitiveSetList& primitives = geometry->getPrimitiveSetList(); |
| | 73 | for(osg::Geometry::PrimitiveSetList::iterator itr = primitives.begin(); |
| | 74 | itr != primitives.end(); |
| | 75 | ++itr) |
| | 76 | { |
| | 77 | osg::DrawArrays* drawArray = dynamic_cast<osg::DrawArrays*>(itr->get()); |
| | 78 | if (drawArray && drawArray->getMode()==GL_POLYGON) |
| | 79 | { |
| | 80 | computeBoundaryAngles(*vertices, drawArray->getFirst(), drawArray->getCount()); |
| | 81 | } |
| | 82 | } |
| | 83 | } |
| | 84 | |
| | 85 | osg::Vec3 computeNewVertexPosition(osg::Vec3& v1, osg::Vec3& v2, osg::Vec3& v3) |
| | 86 | { |
| | 87 | double angle = computeAngle(v1,v2,v3); |
| | 88 | osg::Vec3 v21(v2-v1); |
| | 89 | osg::Vec3 v32(v3-v2); |
| | 90 | v21.normalize(); |
| | 91 | v32.normalize(); |
| | 92 | osg::Vec3 cross = v21^v32; |
| | 93 | |
| | 94 | float t = 5.0; |
| | 95 | |
| | 96 | osg::Vec3 bisector(v32-v21); |
| | 97 | |
| | 98 | if (bisector.length()<0.5) |
| | 99 | { |
| | 100 | // angle wider than 90 degrees so use side vectors as guide for angle to project along. |
| | 101 | osg::Vec3 s21 = v21 ^ osg::Vec3(0.0f,0.0f,1.0f); |
| | 102 | s21.normalize(); |
| | 103 | |
| | 104 | osg::Vec3 s32 = v32 ^ osg::Vec3(0.0f,0.0f,1.0f); |
| | 105 | s32.normalize(); |
| | 106 | |
| | 107 | osg::Vec3 bisector(s21+s32); |
| | 108 | bisector.normalize(); |
| | 109 | |
| | 110 | float l = t / sin(angle*0.5); |
| | 111 | |
| | 112 | osg::Vec3 new_vertex = v2 + bisector * l; |
| | 113 | new_vertex.z() += 0.5f; |
| | 114 | |
| | 115 | return new_vertex; |
| | 116 | } |
| | 117 | else |
| | 118 | { |
| | 119 | float l = t / sin(angle*0.5); |
| | 120 | |
| | 121 | bisector.normalize(); |
| | 122 | if (cross.z()>0.0) bisector = -bisector; |
| | 123 | |
| | 124 | osg::Vec3 new_vertex = v2 + bisector * l; |
| | 125 | new_vertex.z() += 0.5f; |
| | 126 | return new_vertex; |
| | 127 | } |
| | 128 | } |
| | 129 | |
| | 130 | osg::PrimitiveSet* computeBevelEdge(osg::Vec3Array& orig_vertices, unsigned int start, unsigned int count, osg::Vec3Array& new_vertices) |
| | 131 | { |
| | 132 | OSG_NOTICE<<"computeBoundaryAngles("<<orig_vertices.size()<<", "<<start<<", "<<count<<")"<<std::endl; |
| | 133 | if (orig_vertices[start+count-1]==orig_vertices[start]) |
| | 134 | { |
| | 135 | OSG_NOTICE<<"is a line loop"<<std::endl; |
| | 136 | } |
| | 137 | else |
| | 138 | { |
| | 139 | OSG_NOTICE<<"is not line loop, ("<<orig_vertices[start+count-1]<<"), ("<<orig_vertices[start]<<")"<<std::endl; |
| | 140 | return new osg::DrawArrays(GL_POLYGON, start, count); |
| | 141 | } |
| | 142 | |
| | 143 | new_vertices[start] = computeNewVertexPosition(orig_vertices[start+count-2],orig_vertices[start],orig_vertices[start+1]); |
| | 144 | for(unsigned int i=start+1; i<start+count-1; ++i) |
| | 145 | { |
| | 146 | new_vertices[i] = computeNewVertexPosition(orig_vertices[i-1],orig_vertices[i],orig_vertices[i+1]); |
| | 147 | } |
| | 148 | new_vertices[start+count-1] = computeNewVertexPosition(orig_vertices[start+count-2],orig_vertices[start],orig_vertices[start+1]); |
| | 149 | |
| | 150 | return new osg::DrawArrays(GL_POLYGON, start, count); |
| | 151 | } |
| | 152 | |
| | 153 | osg::Geometry* computeBevelEdge(osg::Geometry* orig_geometry) |
| | 154 | { |
| | 155 | OSG_NOTICE<<"computeBoundaryAngles("<<orig_geometry<<")"<<std::endl; |
| | 156 | osg::Vec3Array* orig_vertices = dynamic_cast<osg::Vec3Array*>(orig_geometry->getVertexArray()); |
| | 157 | osg::Geometry::PrimitiveSetList& orig_primitives = orig_geometry->getPrimitiveSetList(); |
| | 158 | |
| | 159 | osg::Geometry* new_geometry = new osg::Geometry; |
| | 160 | osg::Vec3Array* new_vertices = new osg::Vec3Array(*orig_vertices); |
| | 161 | osg::Geometry::PrimitiveSetList& new_primitives = new_geometry->getPrimitiveSetList(); |
| | 162 | new_geometry->setVertexArray(new_vertices); |
| | 163 | osg::Vec4Array* new_colours = new osg::Vec4Array; |
| | 164 | new_colours->push_back(osg::Vec4(1.0,0.0,0.0,1.0)); |
| | 165 | new_geometry->setColorArray(new_colours); |
| | 166 | new_geometry->setColorBinding(osg::Geometry::BIND_OVERALL); |
| | 167 | |
| | 168 | for(osg::Geometry::PrimitiveSetList::iterator itr = orig_primitives.begin(); |
| | 169 | itr != orig_primitives.end(); |
| | 170 | ++itr) |
| | 171 | { |
| | 172 | osg::DrawArrays* drawArray = dynamic_cast<osg::DrawArrays*>(itr->get()); |
| | 173 | if (drawArray && drawArray->getMode()==GL_POLYGON) |
| | 174 | { |
| | 175 | new_primitives.push_back(computeBevelEdge(*orig_vertices, drawArray->getFirst(), drawArray->getCount(), *new_vertices)); |
| | 176 | } |
| | 177 | } |
| | 178 | return new_geometry; |
| | 179 | } |
| | 180 | |
| | 181 | int main(int argc, char** argv) |
| | 182 | { |
| | 183 | osg::ArgumentParser arguments(&argc, argv); |
| | 184 | |
| | 185 | if (arguments.read("--test")) |
| | 186 | { |
| | 187 | return main_test(argc,argv); |
| | 188 | } |
| | 189 | else if (arguments.read("--original") || arguments.read("--orig")) |
| | 190 | { |
| | 191 | return main_orig(argc,argv); |
| | 192 | } |
| | 193 | |
| | 194 | std::string fontFile("arial.ttf"); |
| | 195 | while(arguments.read("-f",fontFile)) {} |
| | 196 | |
| | 197 | std::string word("This is a simple test"); |
| | 198 | while(arguments.read("-w",word)) {} |
| | 199 | |
| | 200 | osg::ref_ptr<osgText::Font3D> font = osgText::readFont3DFile(fontFile); |
| | 201 | if (!font) return 1; |
| | 202 | OSG_NOTICE<<"Read font "<<fontFile<<" font="<<font.get()<<std::endl; |
| | 203 | |
| | 204 | |
| | 205 | bool useTessellator = false; |
| | 206 | while(arguments.read("-t") || arguments.read("--tessellate")) { useTessellator = true; } |
| | 207 | |
| | 208 | osg::ref_ptr<osg::Group> group = new osg::Group; |
| | 209 | osg::Vec3 position; |
| | 210 | |
| | 211 | for(unsigned int i=0; i<word.size(); ++i) |
| | 212 | { |
| | 213 | osg::ref_ptr<osgText::Font3D::Glyph3D> glyph = font->getGlyph(word[i]); |
| | 214 | if (!glyph) return 1; |
| | 215 | |
| | 216 | osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform; |
| | 217 | transform->setPosition(position); |
| | 218 | transform->setAttitude(osg::Quat(osg::inDegrees(90.0),osg::Vec3d(1.0,0.0,0.0))); |
| | 219 | |
| | 220 | position.x() += glyph->getHorizontalWidth(); |
| | 221 | |
| | 222 | osg::ref_ptr<osg::Geode> geode = new osg::Geode; |
| | 223 | |
| | 224 | osg::Vec3Array* vertices = glyph->getRawVertexArray(); |
| | 225 | osg::Geometry::PrimitiveSetList& primitives = glyph->getRawFacePrimitiveSetList(); |
| | 226 | |
| | 227 | osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry; |
| | 228 | geometry->setVertexArray(vertices); |
| | 229 | geometry->setPrimitiveSetList(primitives); |
| | 230 | osg::Vec4Array* colours = new osg::Vec4Array; |
| | 231 | colours->push_back(osg::Vec4(1.0,1.0,1.0,1.0)); |
| | 232 | geometry->setColorArray(colours); |
| | 233 | geometry->setColorBinding(osg::Geometry::BIND_OVERALL); |
| | 234 | |
| | 235 | computeBoundaryAngles(geometry); |
| | 236 | |
| | 237 | osg::Geometry* bevel = computeBevelEdge(geometry); |
| | 238 | geode->addDrawable(bevel); |
| | 239 | |
| | 240 | if (useTessellator) |
| | 241 | { |
| | 242 | osgUtil::Tessellator ts; |
| | 243 | ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE); |
| | 244 | ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); |
| | 245 | ts.retessellatePolygons(*geometry); |
| | 246 | |
| | 247 | ts.retessellatePolygons(*bevel); |
| | 248 | |
| | 249 | } |
| | 250 | |
| | 251 | geode->addDrawable(geometry.get()); |
| | 252 | |
| | 253 | transform->addChild(geode.get()); |
| | 254 | |
| | 255 | group->addChild(transform.get()); |
| | 256 | } |
| | 257 | |
| | 258 | std::string filename; |
| | 259 | if (arguments.read("-o", filename)) osgDB::writeNodeFile(*group, filename); |
| | 260 | |
| | 261 | osgViewer::Viewer viewer(arguments); |
| | 262 | viewer.setSceneData(group.get()); |
| | 263 | viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); |
| | 264 | return viewer.run(); |
| | 265 | } |